콘솔 로그를 찍어가며 값을 확인하는 형태.
어떠한 명령을 일으켜서 함수 외부에 있는 어떤 영역의 변화를 직접적으로 일으킨다.
const products = [
{name: '반팔티', price: 15000},
{name: '긴팔티', price: 20000},
{name: '반바지', price: 15000},
{name: '청바지', price: 45000},
{name: '후드티', price: 35000}
];
// 직접 이름 수집
let names = [];
for (const p of products){
names.push(p.name);
}
console.log(names);
Array 인스턴스의 map() 메서드는 호출한 배열의 모든 요소에 주어진 함수를 호출한 결과로 채운 새로운 배열을 생성합니다.
함수형 프로그래밍에서는 함수가 인자와 리턴 값으로 소통하는 것을 권장한다. map은 결과값을 리턴 받는다.
map(callbackFn, thisArg)
배열의 각 요소에 대해 실행할 함수입니다. 반환 값은 새 배열에서 단일 요소로 추가됩니다. 이 함수는 다음 인수로 호출합니다. 다음 세 가지 인수를 가집니다.
Parameters | description |
---|---|
currentValue(element) | 배열 내에서 처리할 현재 요소 |
index | 배열 내에서 처리할 현재 요소의 인덱스 |
array | map()을 호출한 배열 |
callback을 실행할 때 this로 사용되는 값. 순회 메서드를 참고하시기 바랍니다.
배열의 각 요소에 대해 실행한 callback의 결과를 모은 새로운 배열.
👽 example 1)
const products = [
{name: '반팔티', price: 15000},
{name: '긴팔티', price: 20000},
{name: '반바지', price: 15000},
{name: '청바지', price: 45000},
{name: '후드티', price: 35000}
];
// 추상화된 수집 함수
const map = (func, iterable) => {
let response = [];
for (const a of iterable){
response.push(func(a));
}
return response;
}
console.log(map(p => p.name, products));
👽 example 2)
const array1 = [1,2,4,47];
const map1 = array1.map((x) => x * 2); // Pass a function to map
console.log(map1); // Expected output: Array [2, 8, 18, 32]
👽 example 3)
다음 코드는 숫자 배열을 받아서 배열 안의 각 숫자의 제곱근이 들어있는 새로운 배열을 만듭니다.
const numbers = [1, 4, 9];
const roots = numbers.map((num) => Math.sqrt(num));
// roots 는 이제 [1, 2, 3] 입니다.
// numbers 는 여전히 [1, 4, 9] 입니다.
👽 example 4)
다음 코드는 객체의 배열을 받아 각 객체를 다른 형태로 재구성해 새로운 배열을 만듭니다.
const kvArray = [
{ key: 1, value: 10 },
{ key: 2, value: 20 },
{ key: 3, value: 30 },
];
const reformattedArray = kvArray.map(({ key, value }) => ({ [key]: value }));
console.log(reformattedArray); // [{ 1: 10 }, { 2: 20 }, { 3: 30 }]
console.log(kvArray);
// [
// { key: 1, value: 10 },
// { key: 2, value: 20 },
// { key: 3, value: 30 }
// ]
👽 example 5)
콜백을 하나의 인수(순회 중인 요소)와 함께 사용하는 것이 일반적입니다. 특정 함수는 추가적인 선택적 인수를 사용하더라도 하나의 인수와 함께 사용하는 것이 일반적입니다. 이러한 습관은 혼란스러운 행동으로 이어질 수 있습니다. 아래 코드를 고려해 보세요.
Copy to Clipboard
["1", "2", "3"].map(parseInt);
누군가는 [1, 2, 3]을 기대할 수 있지만, 실제 결과는 [1, NaN, NaN]입니다.
parseInt는 종종 하나의 인수와 함께 사용되지만, 두 개의 인수를 받습니다. 첫 번째 인수는 표현식이고 두 번째 인수는 콜백 함수의 지수입니다. Array.prototype.map은 요소, 인덱스, 배열의 세 가지 인수를 전달합니다. 세 번째 인수는 parseInt에 의해 무시되지만, 두 번째 인수는 무시되지 않습니다. 이것이 혼란의 원인일 수 있습니다.
👽 example 6)
/* 첫 번째 순회 (인덱스는 0입니다): */ parseInt("1", 0); // 1
/* 두 번째 순회 (인덱스는 1입니다): */ parseInt("2", 1); // NaN
/* 세 번째 순회 (인덱스는 2입니다): */ parseInt("3", 2); // NaN
👽 example 7)
["1", "2", "3"].map((str) => parseInt(str, 10)); // [1, 2, 3]
👽 example 8)
["1", "2", "3"].map(Number); // [1, 2, 3]
// parseInt()와는 다르게 Number()는 부동소수 혹은 (해결된) 지수 표기법을 반환합니다.
["1.1", "2.2e2", "3e300"].map(Number); // [1.1, 220, 3e+300]
// 대조적으로 만약 위 배열에 parseInt()를 사용했을 경우입니다
["1.1", "2.2e2", "3e300"].map((str) => parseInt(str, 10)); // [1, 2, 3]
보다 자세한 논의는 Allen Wirfs-Brock의 JavaScript 선택적 인수 위험를 보시기 바랍니다.
👽 example 9)
undefined 또는 아무것도 반환되지 않으면 결과 배열에 undefined가 포함됩니다. 대신 요소를 삭제하려면 filter() 메서드를 체인으로 연결하거나 flatMap() 메서드를 사용하여 빈 배열을 반환하여 삭제를 나타냅니다.
const numbers = [1, 2, 3, 4];
const filteredNumbers = numbers.map((num, index) => {
if (index < 3) {
return num;
}
});
// 인덱스가 0부터 시작하기 때문에 filterNumber는 1, 2, 3과 undefined 입니다.
// filteredNumbers는 [1, 2, 3, undefined]입니다
// numbers는 여전히 [1, 2, 3, 4]입니다
콜백은 부작용이 있습니다.
👽 example 10)
Copy to Clipboard
const cart = [5, 15, 25];
let total = 0;
const withTax = cart.map((cost) => {
total += cost;
return cost * 1.2;
});
console.log(withTax); // [6, 18, 30]
console.log(total); // 45
👽 example 11)
Copy to Clipboard
const cart = [5, 15, 25];
const total = cart.reduce((acc, cost) => acc + cost, 0);
const withTax = cart.map((cost) => cost * 1.2);
👽 example 12)
Copy to Clipboard
const products = [
{ name: "sports car" },
{ name: "laptop" },
{ name: "phone" },
];
products.map((product) => {
product.price = 100;
});
👽 example 13)
위에서 언급했듯이, 이는 안티 패턴입니다. map()의 반환 값을 사용하지 않는다면 forEach() 혹은 for...of 반복문을 대신 사용하시기 바랍니다.
Copy to Clipboard
products.forEach((product) => {
product.price = 100;
});
혹은 대신 새로운 배열을 생성할 수 있습니다.
js
Copy to Clipboard
const productsWithPrice = products.map((product) => {
return { ...product, price: 100 };
});
👽 example 14)
array 인수는 배열을 참조하는 기존 변수가 없는 경우, 특히 배열의 다른 요소에 접근하고 싶을 때 유용합니다. 다음 예제에서는 먼저 filter()를 사용하여 양수 값을 추출한 다음 map()을 사용하여 각 요소가 이웃과 자신의 평균인 새 배열을 만듭니다.
const numbers = [3, -1, 1, 4, 1, 5, 9, 2, 6];
const averaged = numbers
.filter((num) => num > 0)
.map((num, idx, arr) => {
// arr 안수 없이, 중간 배열을 변수로 저장하지 않고 중간 배열에
// 쉽게 접근할 수 있는 방법이 없습니다.
const prev = arr[idx - 1];
const next = arr[idx + 1];
let count = 1;
let total = num;
if (prev !== undefined) {
count++;
total += prev;
}
if (next !== undefined) {
count++;
total += next;
}
const average = total / count;
// 소수점 두 자리를 유지합니다
return Math.round(average * 100) / 100;
});
console.log(averaged); // [2, 2.67, 2, 3.33, 5, 5.33, 5.67, 4]
array 인수는 생성 중인 배열이 아닙니다. 콜백 함수에서 생성 중인 배열에 접근할 방법이 없습니다.
👽 example 15)
희소 배열은 map() 이후에도 그대로 희소 배열로 남습니다. 빈 슬롯의 인덱스는 반환된 배열에서도 여전히 빈 슬롯으로 존재하며 콜백 함수도 해당 빈 슬롯에서는 호출되지 않습니다.
Copy to Clipboard
console.log(
[1, , 3].map((x, index) => {
console.log(`Visit ${index}`);
return x * 2;
}),
);
// 0 방문
// 2 방문
// [2, empty, 6]
👽 example 16)
map() 메서드는 this의 length 속성을 읽어서 음수가 아닌 length보다 작은 정수 키를 가진 각각의 속성에 접근합니다.
const arrayLike = {
length: 3,
0: 2,
1: 3,
2: 4,
3: 5, // length가 3이기 때문에 map()에서 무시합니다
};
console.log(Array.prototype.map.call(arrayLike, (x) => x ** 2));
// [ 4, 9, 16 ]
👽 example 17)
이 예제는 querySelectorAll에서 수집한 객체 컬렉션을 순회하는 방법을 보여줍니다. 이는 querySelectorAll이 객체 컬렉션인 NodeList를 반환하기 때문입니다. 이 경우 아래 코드와 같이 선택한 모든 option 값을 화면에 반환합니다.
Copy to Clipboard
const elems = document.querySelectorAll("select option:checked");
const values = Array.prototype.map.call(elems, ({ value }) => value);
elems를 객체로 변환하기 위해 Array.from()를 사용할 수 있습니다. 그리고 나서 map() 메서드로 접근합니다.
참고 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/map