4 배열생성
4.1 배열 리터럴
const arr = [1,2,3]
console.log(arr.length); // 3
const arr = [1,,3]
console.log(arr.length); // 3
console.log(arr); // [1, empty, 3]
희소배열을 생성할 수 있다.
4.2 Array 생성자 함수
const arr = new Array(10);
console.log(arr); // [empty * 10]
console.log(arr.length); // 10
전달된 인수가 1개이고 숫자인 경우 length 프로퍼티 값이 인수인 배열을 생성한다.
const arr = new Array(10);
console.log(arr); // [empty * 10]
console.log(arr.length); // 10
console.log(Object.getOwnPropertyDescriptor(arr)); // undefined
이때 생성된 배열은 희소 배열이다. length 프로퍼티 값은 0이 아니지만 실제로 배열의 요소는 존재하지 않는다.
new Array(); // => []
전달된 인수가 없는 경우 빈 배열을 생성한다.
4.3 Array.of
ES6에서 도입된 Array.of 메소드는 전달된 인수를 요소로 갖는 배열을 생성한다.
// 전달된 인수가 1개이고 숫자이더라도 인수를 요소로 갖는 배열을 생성한다.
Array.of(1); // -> [1]
Array.of(1, 2, 3); // -> [1, 2, 3]
Array.of('string'); // -> ['string']
4.4 Array.from
ES6에서 도입된 Array.from 메소드는 유사 배열 객체 또는 인터러블 객체를 인수로 전달받아 배열로 변환하여 만든다.
// 유사 배열 객체를 변환하여 배열을 생성한다.
Array.from({length: 2, 0: 'a', 1: 'b'}); // -> ['a', 'b']
// 이터러블을 변환하여 배열을 생성한다. 문자열은 인터러블이다.
Array.from('Hello'); // -> ['H', 'e', 'l', 'l', 'o']
length를 전달해줄 경우 해당 크기의 배열이 생성이 된다.
// Array.from에 length만 존재하는 유사 배열 객체를 전달하면 undefined를 요소로 채운다.
Array.from({length : 3}); // => [undefined, undefined, undefined]
// Array.from은 두 번째 인수로 전달한 콜백 함수의 반환값으로 구성된 배열을 반환한다.
Array.from({ length:3 }, (_, i) => i); // => [0, 1, 2]
유사 배열 객체와 이터러블 객체
유사 배열 객체(array-like object)는 마치 배열처럼 인덱스로 프로퍼티 값에 접근할 수 있고 length 프로퍼티를 갖는객체를 말한다. 유사 배열 객체는 마치 배열처럼 for문으로 순회가능하다.
// 유사 배열 객체
const arraylike = {
'0': 'apple',
'1': 'banana',
'2': 'orange',
length : 3
};
// 유사 배열 객체는 마치 배열처럼 for문으로 순회할 수도 있다.
for(let i = 0; i < arraylike.length; i++){
console.log(arraylike[i]);
}
이터러블 객체는 Symbol.iterator 메소드를 구현하여 for ... of 문으로 순회할 수 있으며, 스프레드 문법과 배열 디스트럭처링 할당의 대상으로 사용할 수 있는 객체를 말한다.
9 배열 고차함수
배열의 요소들을 순서에 맞게 정렬해주기 (Array.sort( ))
const months = ['March', 'Jan', 'Feb', 'Dec'];
months.sort();
console.log(months);
// Expected output: Array ["Dec", "Feb", "Jan", "March"]
const array1 = [1, 30, 4, 21, 100000];
array1.sort();
console.log(array1);
// Expected output: Array [1, 100000, 21, 30, 4]
객체를 요소로 갖는 배열을 정렬하기 (compare)
// 객체를 요소로 갖는 배열을 정렬하는 예제
const todos = [
{ id: 4, content: "JavaScript" },
{ id: 1, content: "HTML" },
{ id: 2, content: "CSS" },
];
// 비교함수. 매개변수 key는 프로퍼티 키다.
function compare(key) {
// 프로퍼티 값이 문자열인 경우 - 산술연산으로 비교하면 NaN이 나오므로 비교 연산을 사용할 수 없다.
// 비교 함수는 양수/음수/0을 반환하면 되므로 - 산술 연산 대신 비교 연산을 사용할 수 있다.
return (a, b) => (a[key] > b[key] ? 1 : a[key] < b[key] ? -1 : 0);
}
// id를 기준으로 오름차순 정렬
todos.sort(compare("id"));
console.log(todos);
/*
[
{ id: 1, content: 'HTML' },
{ id: 2, content: 'CSS' },
{ id: 4, content: 'JavaScript' }
]
*/
// content를 기준으로 오름차순 정렬
todos.sort(compare("content"));
console.log(todos);
/*
[
{ id: 2, content: 'CSS' },
{ id: 1, content: 'HTML' },
{ id: 4, content: 'JavaScript' }
]
*/
배열의 각각의 요소에 대해 함수를 호출하기 (Array.forEach( ))
const array1 = ['a', 'b', 'c'];
array1.forEach(element => console.log(element));
// Expected output: "a"
// Expected output: "b"
// Expected output: "c"
배열 각각의 콜백 함수를 호출, 이후 새로운 배열로 반환하기 (Array.map( ))
const array1 = [1, 4, 9, 16];
// Pass a function to map
const map1 = array1.map(x => x * 2);
console.log(map1);
// Expected output: Array [2, 8, 18, 32]
배열내 조건에 맞는 값들만 받아오기 (Array.filter( ))
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
console.log(result);
// Expected output: Array ["exuberant", "destruction", "present"]
const number = [1, 2, 3, 4, 5];
// filter 메소드는 number 배열의 모든 요소를 순회하면서 콜백 함수를 반복 호출한다.
// 그리고 콜백 함수의 반환값이 true인 요소로만 구성된 새로운 배열을 반환한다.
// 다음의 경우 number 배열에서 홀수인 요소만 필터링한다(1은 true로 평가된다.)
const odds = number.filter((item) => item % 2);
console.log(odds); //[ 1, 3, 5 ]
주어진 콜백 함수를 가산기와 배열 각각에 대해 (왼쪽 > 오른쪽) 으로 호출하여 각각의 값을 하나의 값으로 줄인 결과를 반환하기 (Array.reduce( ))
const array1 = [1, 2, 3, 4];
// 0 + 1 + 2 + 3 + 4
const initialValue = 0;
const sumWithInitial = array1.reduce(
(accumulator, currentValue) => accumulator + currentValue,
initialValue
);
console.log(sumWithInitial);
// Expected output: 10
평균 구하기
// 평균 구하기
const values = [1, 2, 3, 4, 5, 6];
const average = values.reduce((acc, cur, i, { length }) => {
// 마지막 순회가 아니면 누적값을 반환하고 마지막 순회면 평균구해 반환한다.
return i === length - 1 ? (acc + cur) / length : acc + cur;
}, 0);
console.log(average);
최대값 구하기
// 최대값 구하기
const values = [1, 2, 3, 4, 5];
const max = values.reduce((acc, cur) => (acc > cur ? acc : cur), 0);
console.log(max);
// 최대값을 구할 때는 reduce 메소드보다 Math.max 메소드를 사용하는 방법이 직관적이다.
const method_max = Math.max(...values);
// var max = Math.max.apply(null, values);
console.log(method_max);
요소의 중복 횟수 구하기
// 요소의 중복 횟수 구하기
const fruits = ["banana", "apple", "orange", "orange", "apple"];
const count = fruits.reduce((acc, cur) => {
// 첫 번째 순회시 acc는 초기값인 {}이고 cur은 첫 번째 요소인 'banana'이다.
// 초기값으로 전달받은 빈 객체에 요소값인 cur을 프로퍼티 키로, 요소의 개수를 프로퍼티 값으로 할당한다.
// 만약 프로퍼티 값이 undefined(처음 등장하는 요소)이면 프로퍼티 값을 1로 초기화 한다.
acc[cur] = (acc[cur] || 0) + 1;
return acc;
}, {});
// 콜백 함수는 총 5번 호출되고 다음과 같이 결과 값을 반환한다.
/*
{banana : 1} => {banana : 1, apple : 1} => {banana : 1, apple : 1, orange : 1}
=> {banana : 1, apple : 1, orange : 2} => {banana : 1, apple : 2, orange : 2}
*/
console.log(count);
//
중첩 배열 평탄화
// 중첩 배열 평탄화
const values = [1, [2, 3], 4, [5, 6]];
const flatten = values.reduce((acc, cur) => acc.concat(cur), []);
// [1] => [1, 2, 3] => [1, 2, 3, 4] => [1, 2, 3, 4, 5, 6]
console.log(flatten); // [ 1, 2, 3, 4, 5, 6 ]
// 평탄화 할때는 Array.prototype.flat 메소드가 더 직관적이고 효율적이다.
[1, [2, 3, 4, 5]].flat(); // => [1, 2, 3, 4, 5]
// 인수 2는 중첩 배열을 평탄화하기 위한 깊이 값이다.
[1, [2, 3, [4, 5]]].flat(2); // => [1, 2, 3, 4, 5]
중복 요소 제거
// 중복 요소 제거
const values = [1, 2, 1, 3, 5, 4, 5, 3, 4, 4];
const result = values.reduce(
(unique, val, i, _values) =>
// 현재 순회중인 요소의 인덱스 i가 val의 인덱스와 같다면 val은 처음 순회하는 요소이다.
// 현재 순회중인 요소의 인덱스 i가 val의 인덱스와 다르다면 val은 중복된 요소이다.
// 처음 순회하는 요소만 초기값 []가 전달된 unique 배열에 담아 반환하면 중복된 요소는 제거된다.
_values.indexOf(val) === i ? [...unique, val] : unique,
[]
);
console.log(result); // [ 1, 2, 3, 5, 4 ]
// 중복 요소를 제거할때는 filter 메소드를 사용하는 방법이 더 직관적이다.
const value_1 = [1, 2, 1, 3, 5, 4, 5, 3, 4, 4];
// 현재 순회중인 요소의 인덱스 i가 val의 인덱스와 같다면 val은 처음 순회하는 요소다. 이 요소만 필터링 된다.
const result_2 = value_1.filter((val, i, value_1) => value_1.indexOf(val) === i);
console.log(result_2);
// 중복은 허용하지 않는 Set 객체의 특성을 활용해서 중복된 요소를 제거할 수도 있다.
const value_2 = [1, 2, 1, 3, 5, 4, 5, 3, 4, 4];
const result_3 = [...new Set(value_2)];
console.log(result_3);
객체의 프로퍼티 값을 합산하는 경우
// 객체의 프로퍼티 값을 합산하는 경우 반드시 초기값을 전달해야 한다.
const products = [
{ id: 1, price: 100 },
{ id: 2, price: 200 },
{ id: 3, price: 300 },
];
/*
1번째 순회 : acc => 0, cur => { id:1, price: 100 }
1번째 순회 : acc => 100, cur => { id:2, price: 100 }
1번째 순회 : acc => 300, cur => { id:3, price: 100 }
*/
const priceSum = products.reduce((acc, cur) => acc + cur.price, 0);
console.log(priceSum); // 600
배열내 값들이 조건을 모두 만족하는지 검사하기 (Array.prototype.every)
const isBelowThreshold = (currentValue) => currentValue < 40;
const array1 = [1, 30, 39, 29, 10, 13];
console.log(array1.every(isBelowThreshold));
// Expected output: true
정해진 조건을 만족하는 첫번째 요소를 반환하기 (Array.prototype.find( ))
const array1 = [5, 12, 8, 130, 44];
const found = array1.find(element => element > 10);
console.log(found);
// Expected output: 12
find( )와 filter( )의 차이
// filter 메소드는 배열을 반환한다.
[1,2,2,3].filter(item => item === 2); // [2, 2]
// find 메소드는 요소를 반환한다.
[1,2,2,3].find(item => item === 2); // 2
정해진 조건을 만족하는 첫 요소의 인덱스를 반환하기 (Array.findIndex( ))
const array1 = [5, 12, 8, 130, 44];
const isLargeNumber = (element) => element > 13;
console.log(array1.findIndex(isLargeNumber));
// Expected output: 3
const users = [
{ id: 1, name: "Lee" },
{ id: 2, name: "Kim" },
{ id: 3, name: "Choi" },
{ id: 4, name: "Park" },
];
// id가 2인 요소의 인덱스를 구한다.
users.findIndex((user) => user.id === 2); // => 1
// name이 Park 인 요소의 인덱스를 구한다.
users.findIndex((user) => user.name === "Park"); // => 3
// 위와 같이 프로퍼티 키와 프로퍼티 값으로 요소의 인덱스를 구하는 경우 다음과 같이 콜백 함수를 추상화 할 수 있다.
function predicate(key, value) {
// key와 value를 기억하는 클로저를 반환
return (item) => item[key] === value;
}
// id가 2인 요소의 인덱스를 구한다.
users.findIndex(predicate("id", 2)); // => 1
// name가 Park인 요소의 인덱스를 구한다.
users.findIndex(predicate("name", "Park")); // => 3
map 메소드를 통해 생성된 새로운 배열을 평탄화하기 (Array.flatMap())
let arr1 = ["it's Sunny in", "", "California"];
arr1.map(x => x.split(" "));
// [["it's","Sunny","in"],[""],["California"]]
arr1.flatMap(x => x.split(" "));
// ["it's","Sunny","in","California"]
const arr = ["hello", "world"];
// map과 flat을 순차적으로 실행
arr.map((x) => x.split("")).flat();
// => ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']
// flatMap은 map을 통해 생성된 새로운 배열을 평탄화 한다.
arr.flatMap((x) => x.split(""));
// => ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']
단 flatMap( ) 메소드는 flat( ) 메소드와 달리 평탄화 깊이를 지정할 수 없어 1단계만 평탄화 한다. 평탄화 깊이를 지정하려면 map 메소드와 flat 메소드를 각각 호출한다.
const arr = ["hello", "world"];
// flatMap은 1단계만 평탄화 한다.
arr.flatMap((str, index) => [index, [str, str.length]]);
// -> [[0, ['hello', 5]], [1, ['world', 5]]] => [0, ['hello', 5], 1, ['world', 5]]
// 평탄화 깊이를 지정해야 하면 flatMap 메소드를 사용하지 말고 map 메소드와 flat 메소드를 각각 호출한다.
arr.map((str, index) => [index, [str, str.length]]).flat(2);
// -> [[0, ['hello', 5]], [1, ['world', 5]]] =>[0, 'hello', 5, 1, 'world', 5]