배열
배열이란?
자바스크립트의 모든 값을 배열의 요소가 될 수 있음.
배열은 객체타입
일반 객체와 배열을 구분하는 가장 명확한 차이는 '값의 순서’와 ‘length’ 프로퍼티
자바스크립트 배열은 배열이 아니다
해시 테이블로 구현된 객체이다.
모던 자바스크립트 엔진은 배열을 일반 객체와 구별하여 좀 더 배열처럼 동작하도록 최적화가 되어있다. (일반객체보다 더 빠르다는 이야기)
length 프로퍼티와 희소 배열
length 값보다 작은 숫자를 할당하면 배열의 크기가 줄어듬
반대로 큰 숫자를 할당해도 크기가 늘어나진 않음.
const sparse = [, 2, , 4];
// 희소 배열의 length 프로퍼티 값은 요소의 개수와 일치하지 않는다.
console.log(sparse.length); // 4
console.log(sparse); // [empty, 2, empty, 4]
// 배열 sparse에는 인덱스가 0, 2인 요소가 존재하지 않는다.
console.log(Object.getOwnPropertyDescriptors(sparse));
희소 배열을 쓰지 않도록 주의할 것
배열 생성
// 전달된 인수가 1개이고 숫자이더라도 인수를 요소로 갖는 배열을 생성한다.
Array.of(1); // [1]
Array.of(1, 2, 3); // [1, 2, 3]
Array.of('string'); // ['string']
// 유사 배열 객체를 변환하여 배열을 생성한다.
Array.from({ length: 2, 0: 'a', 1: 'b' }); // ['a', 'b']
// 이터러블을 변환하여 배열을 생성한다. 문자열은 이터러블이다.
Array.from('Hello'); // ['H', 'e', 'l', 'l', 'o']
// Array.from에 length만 존재하는 유사 배열 객체를 전달하면 undefined를 요소로 채운다.
Array.from({ length: 3 }); // [undefined, undefined, undefined]
// Array.from은 두 번째 인수로 전달한 콜백 함수의 반환값으로 구성된 배열을 반환한다.
Array.from({ length: 3 }, (_, i) => i); // [0, 1, 2]
배열요소의 삭제
delete 를 쓰면 희소 배열이 되므로 splice를 사용하여 제거
const arr = [1, 2, 3];
// Array.prototype.splice(삭제를 시작할 인덱스, 삭제할 요소 수)
// arr[1]부터 1개의 요소를 제거
arr.splice(1, 1);
console.log(arr); // [1, 3]
// length 프로퍼티가 자동 갱신된다.
console.log(arr.length); // 2
배열 메서드
원본을 반환하는 놈과 새로운 배열을 반환하는 놈이 있음.
arr.push(2); //원본 변경
arr.concat(3); //새로운 배열 반환
isArray 배열 여부
indexOf 인수 검색 후 인덱스 반환. 요소 검색 시엔 includes로 가독성을 올리자!
push 원본의 마지막에 요소 추가
pop 원본의 마지막 요소 삭제
unshift 인수로 받은 값을 원본 배열의 선두에 요소로 추가함. 스프레드 문법을 사용하는게 더 좋다함.
shift 원본의 첫번째 요소를 제거함
concat 새로운 배열을 반환. 원본의 마지막 요소로 추가. 배열의 경우 해체하여 배열의 요소로 추가. 스프레드 문법으로 대체
결론 : push, unshift, concat 말고 스프레드를 쓰자
let result = [1, 2].concat([3, 4]);
console.log(result); // [1, 2, 3, 4]
// concat 메서드는 ES6의 스프레드 문법으로 대체할 수 있다.
result = [...[1, 2], ...[3, 4]];
console.log(result); // [1, 2, 3, 4]
splice 원본 중간에 요소 추가나 제거
const arr = [1, 2, 3, 4];
// 원본 배열의 인덱스 1부터 2개의 요소를 제거하고 그 자리에 새로운 요소 20, 30을 삽입한다.
const result = arr.splice(1, 2, 20, 30);
// 제거한 요소가 배열로 반환된다.
console.log(result); // [2, 3]
// splice 메서드는 원본 배열을 직접 변경한다.
console.log(arr); // [1, 20, 30, 4]
const arr = [1, 2, 3, 4];
// 원본 배열의 인덱스 1부터 0개의 요소를 제거하고 그 자리에 새로운 요소 100을 삽입한다.
const result = arr.splice(1, 0, 100);
// 원본 배열이 변경된다.
console.log(arr); // [1, 100, 2, 3, 4]
// 제거한 요소가 배열로 반환된다.
console.log(result); // []
특정 요소 제거시엔 indexOf를 통해 인덱스 취득 후 splice로 제거
filter로 제거 가능하지만 이 경우엔 중복 요소가 모두 삭제됨
slice 인수로 전달된 범위의 요소들을 복사하여 배열로 반환한다. 원본 배열은 변경되지 않는다.
첫번째 인수가 음수면 배열의 끝부터
join 구분자로 연결된 문자열 반환
reverse 원본의 순서를 반대로 뒤집음.
fill 인수로 전달받은 값을 요소로 채움
const arr = [1, 2, 3, 4, 5];
// 인수로 전달받은 값 0을 배열의 인덱스 1부터 3 이전(인덱스 3 미포함)까지 요소로 채운다.
arr.fill(0, 1, 3);
// fill 메서드는 원본 배열을 직접 변경한다.
console.log(arr); // [1, 0, 0, 4, 5]
includes 요소포함 여부를 boolean으로 반환.
flat 인수로 전달한 깊이만큼 배열을 평탄화함.
// 중첩 배열을 평탄화하기 위한 깊이 값의 기본값은 1이다.
[1, [2, [3, [4]]]].flat(); // [1, 2, [3, [4]]]
[1, [2, [3, [4]]]].flat(1); // [1, 2, [3, [4]]]
// 중첩 배열을 평탄화하기 위한 깊이 값을 2로 지정하여 2단계 깊이까지 평탄화한다.
[1, [2, [3, [4]]]].flat(2); // [1, 2, 3, [4]]
// 2번 평탄화한 것과 동일하다.
[1, [2, [3, [4]]]].flat().flat(); // [1, 2, 3, [4]]
// 중첩 배열을 평탄화하기 위한 깊이 값을 Infinity로 지정하여 중첩 배열 모두를 평탄화한다.
[1, [2, [3, [4]]]].flat(Infinity); // [1, 2, 3, 4]
배열 고차 함수
sort 배열의 요소를 정렬. 기본 오름차순. 내림차순이 필요할 경우 reverse 사용. 유니코드 포인트 이므로 숫자는 의도대로 정렬되지 않음.
const points = [40, 100, 1, 5, 2, 25, 10];
// 숫자 배열의 오름차순 정렬. 비교 함수의 반환값이 0보다 작으면 a를 우선하여 정렬한다.
points.sort((a, b) => a - b);
console.log(points); // [1, 2, 5, 10, 25, 40, 100]
// 숫자 배열에서 최소/최대값 취득
console.log(points[0], points[points.length - 1]); // 1 100
// 숫자 배열의 내림차순 정렬. 비교 함수의 반환값이 0보다 작으면 b를 우선하여 정렬한다.
points.sort((a, b) => b - a);
console.log(points); // [100, 40, 25, 10, 5, 2, 1]
// 숫자 배열에서 최소/최대값 취득
console.log(points[points.length - 1], points[0]); // 1 100
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'));
forEach 반복문
// forEach 메서드는 콜백 함수를 호출하면서 3개(요소값, 인덱스, this)의 인수를 전달한다.
[1, 2, 3].forEach((item, index, arr) => {
console.log(`요소값: ${item}, 인덱스: ${index}, this: ${JSON.stringify(arr)}`);
});
const numbers = [1, 2, 3];
// forEach 메서드는 원본 배열을 변경하지 않지만 콜백 함수를 통해 원본 배열을 변경할 수는 있다.
// 콜백 함수의 세 번째 매개변수 arr은 원본 배열 numbers를 가리킨다.
// 따라서 콜백 함수의 세 번째 매개변수 arr을 직접 변경하면 원본 배열 numbers가 변경된다.
numbers.forEach((item, index, arr) => { arr[index] = item ** 2; });
console.log(numbers); // [1, 4, 9]
this 바인딩 = 화살표 함수를 쓰자
class Numbers {
numberArray = [];
multiply(arr) {
// 화살표 함수 내부에서 this를 참조하면 상위 스코프의 this를 그대로 참조한다.
arr.forEach(item => this.numberArray.push(item * item));
}
}
const numbers = new Numbers();
numbers.multiply([1, 2, 3]);
console.log(numbers.numberArray); // [1, 4, 9]
map 모든 요소를 순회하면서 콜백 함수를 반복 호출하고 반환값으로 구성된 새로운 배열을 반환. 원본은 변경되지 않는다.
filter 모든 요소를 순회하면서 콜백 함수를 반복 호출하고 반환값이 true인 요소들로 구성된 새로운 배열을 반환. 원본은 변경되지 않는다.
reduce 모든 요소를 순회하면서 콜백 함수를 반복 호출하고 반환값을 다음 순회시 첫번째 인자로 전달하여 하나의 결과값 반환. 원본은 변경되지 않는다.
// 1부터 4까지 누적을 구한다.
const sum = [1, 2, 3, 4].reduce((accumulator, currentValue, index, array) => accumulator + currentValue, 0);
//평균 구하기
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); // 3.5
//요소의 중복횟수 구하기
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;
}, {});
초기값은 생략가능 하지만 객체의 특정 프로퍼티 값을 합산하는 경우에는 반드시 초기값을 전달해야 함.
some 콜백함수의 반환값이 한번이라도 true면 true 아니면 false를 반환함
// 배열의 요소 중 10보다 큰 요소가 1개 이상 존재하는지 확인
[5, 10, 15].some(item => item > 10); // true
// 배열의 요소 중 0보다 작은 요소가 1개 이상 존재하는지 확인
[5, 10, 15].some(item => item < 0); // false
// 배열의 요소 중 'banana'가 1개 이상 존재하는지 확인
['apple', 'banana', 'mango'].some(item => item === 'banana'); // true
// some 메서드를 호출한 배열이 빈 배열인 경우 언제나 false를 반환한다.
[].some(item => item > 3); // false
every 콜백함수의 반환값이 한번이라도 false면 false 아니면 true를 반환함
// 배열의 모든 요소가 3보다 큰지 확인
[5, 10, 15].every(item => item > 3); // true
// 배열의 모든 요소가 10보다 큰지 확인
[5, 10, 15].every(item => item > 10); // false
// every 메서드를 호출한 배열이 빈 배열인 경우 언제나 true를 반환한다.
[].every(item => item > 3); // true
find 반환값이 true인 첫번째 요소를 반환. true가 없으면 undefined 반환
const users = [
{ id: 1, name: 'Lee' },
{ id: 2, name: 'Kim' },
{ id: 2, name: 'Choi' },
{ id: 3, name: 'Park' }
];
// id가 2인 첫 번째 요소를 반환한다. find 메서드는 배열이 아니라 요소를 반환한다.
users.find(user => user.id === 2); // {id: 2, name: 'Kim'}
find의 결과값은 배열이 아닌 요소값
findIndex 반환값이 true인 첫번째 요소의 인덱스를 반환. true가 없으면 -1 반환
flatMap 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]
'공부공부 > JS 딥다이브' 카테고리의 다른 글
[js 딥다이브] 30장 Date (0) | 2024.02.15 |
---|---|
[js 딥다이브] 29장 Math (0) | 2024.02.15 |
[js 딥다이브] 28장 Number (0) | 2024.02.15 |
[js 딥다이브] 26장 ES6 함수의 추가 기능 (0) | 2024.02.15 |
[js 딥다이브] 25장 클래스 (0) | 2024.02.15 |