Arrays
Creating Arrays
Spread operator를 통한 배열 생성은 모든 이터러블 객체에서 가능해요:
console.log([...'0123456789ABCDEF']);
// 중복 원소 없애기
console.log([...new Set([...'hello world'])]);
Array 생성자에 하나의 숫자를 전달하면 length 속성이 해당 숫자로 설정된 array(?)가 반환됩니다:
let a = new Array(10);
console.log(a.length, a[0], 0 in a);
// 이럴 때 말고 언제 쓰는지는 저는 잘 모르겠네요 ㅎ,,,
console.log([...Array(10).keys()].join(' '));
Array.of
는 전달된 인자들을 가지는 배열을 반환하는 팩토리 매서드에요:
console.log(Array.of(1));
console.log(Array.of(1, 2, 3));
Array.form
은 이터러블이나 유사 배열을 배열로 변환하는 팩토리 메서드로 스프레드
연산자와 유사하게 동작해요. Web API에서 종종 유사배열을 반환하기에 이 때
유용하게 사용돼요. 두번째 인자로 함수를 건네줄 수 있는데 상황에 따라 배열의
생성과 매핑을 한번에 할 수 있어 배열 생성 후 별도로 map을 하는 것보다
효율적입니다:
let arraylike = { 0: 1, 1: 2, 2: 3, length: 3 };
console.log(Array.from(arraylike, (x) => 2 * x));
Reading and Writing Array Elements
배열 인덱스(array index)와 객체의 프로퍼티명(object property name)을 구분하는 것이 도움이 됩니다. 모든 배열 인덱스는 프로퍼티명이지만, 프로퍼티명 중에 0이상 2^32-1미만의 정수만 인덱스로 기능할 수 있어요.
Sparse Arrays
배열 표현식에서 쉼표 사이가 비어있으면 희소 배열이 됩니다. undefined인 요소가 있는 것과 희소 배열인 것은 달라요:
let a = [undefined, , undefined];
console.log(0 in a, 1 in a);
console.log(a[0], a[1]);
희소 배열의 length 프로퍼티 값은 배열 요소의 개수보다 큽니다:
let a = new Array(5); // 희소 배열입니다.
console.log(a.length);
a = [];
console.log(a.length);
a[1000] = 0;
console.log(a.length);
희소 배열이 희소(sparse)할수록 느리지만 메모리 효율적인 방식으로 처리됩니다.
Array Length
Invariant: 배열 요소의 인덱스는 항상 length값 미만입니다.
a = [1, 2, 3, 4, 5];
a.length = 3;
console.log(a);
Adding and Deleting Array Elements
재미로만 보는 push와 a[a.length]의 성능 테스트:
let ITER = 123456;
let arr = new Array();
let t = performance.now();
for (let i = 0; i < ITER; i++) {
arr[arr.length] = i;
}
console.log(performance.now() - t);
arr = new Array();
t = performance.now();
for (let i = 0; i < ITER; i++) {
arr.push(i);
}
console.log(performance.now() - t);
요소를 delete한다고 길이가 달라지지는 않습니다:
let a = [1, 2, 3];
delete a[2];
console.log(2 in a, a[2], a.length);
Iterating Arrays
맨날 Object.entries(arr)
로 썼는데 배열이면 아래처럼 바로 쓸 수도 있네요:
for (const [k, v] of [...'abcdef'].entries()) console.log(k, v);
배열에 실제로 존재하는 키만 포함하는 Object.keys()와 달리, keys() 반복자는 누락된 속성을 나타내는 빈 공간을 무시하지 않습니다.
Multidimensional Arrays
희소 배열의 빈 슬롯은 배열 메서드 간에 일관성 없이 동작합니다. 일반적으로 오래된 메서드(forEach 등)는 빈 슬롯을 건너뛰는 반면, 최신 메서드(for/of 등)는 빈 슬롯을 undefined로 처리합니다.
let a = [1, , 3]; a.forEach((i) => console.log(i)); for (const i of a) console.log(i);
Array Iterator Methods
let a = [1, , 3];
a.forEach(
// forEach 외에 다른 대부분의 메서드들도 val, idx, arr을 함수에 건네줍니다.
function (val, idx, arr) {
console.log(val, idx, arr, this);
},
// 다른 대부분의 메서드들도 두번째 인자의 값이 (있다면) 함수 내부의 this가 됩니다.
{ val: 'hello' },
);
forEach와 for문은 유사하지만 for문의 break에 대응되는 문법은 없습니다.
filter 함수는 predicate 함수를 받아요:
희소 배열의 간격 없애기 예제:
console.log([1, , 2, , , 3, 4, , 5, , , 6, , ,].filter(() => true));
수학적 관습?으로 인해 빈 배열에 대해서 every와 some의 결과는 다음과 같습니다:
console.log([].every((x) => true));
console.log([].some((x) => true));
reduce 함수는 첫번째 인자로 지금까지의 누적된 결과를 전달합니다. 함수에 initial value를 전달해주지 않으면 첫번째 요소을 사용합니다:
let ret = [1, , , 2, , , 3].reduce((acc, val, idx, arr) => {
console.log(acc, val, idx, arr);
return acc + val;
}, 0); // 인자 0을 지워보세요
console.log(ret);
[].reduce((acc, val) => acc + val);
reduceRight는 인덱스 내림차순으로 처리합니다. (왜 써본 적이 없지,,,)
reduce/reduceRight는 this를 설정하는 인자가 없습니다. 설정해야된다면 나중에 배울 bind를 쓰면 돼요.
Flattening arrays with flat() and flatMap()
let arr = [1, [2, [3, [4]]]];
console.log(arr.flat());
console.log(arr.flat(1));
console.log(arr.flat(2));
console.log(arr.flat(3));
a.flatMap(f)
는 a.map(f).flat()
과 같지만 더 효율적입니다. 입력 배열의 각
요소마다 N(>=0)개의 요소를 대응시킬 수 있도록 일반화된 map
으로 생각할 수
있어요:
let phrases = ['hello world!', 'hello visitors!'];
console.log(phrases.flatMap((x) => x.split(' ')));
let numbers = [-2, -1, 0, 1, 2];
console.log(numbers.flatMap((x) => (x < 0 ? [] : Math.sqrt(x))));
Adding arrays with concat()
기존 배열을 바꾸지 않아요.
console.log([1, 2].concat([3, [4]], 5));
Stacks and Queues with push(), pop(), shift(), and unshift()
저는 shift랑 unshift를 자꾸 까먹어요:
let a = [1, 2, 3];
console.log(a.shift());
a.unshift(4, 5);
console.log(a);
Subarrays with slice(), splice(), fill(), and copyWithin()
let a = [1, 2, 3, 4, 5];
console.log(a.slice(1, 3));
console.log(a.slice(1));
console.log(a.slice(-3, -1));
splice는 start index, length, 삽입할 요소들을 인자로 받습니다:
let a = [1, 2, 3, 4, 5];
console.log(a.splice(1, 2), a);
console.log(a.splice(0, 2, [1, 2], 3), a);
let a = new Array(5);
console.log(a.fill(0));
console.log(a.fill(9, 1));
console.log(a.fill(8, 2, -1));
copyWithin은 배열 일부분을 배열의 다른 곳으로 옯겨요. 고성능 메서드로 설계되었고 나중에 배울 typed array에 유용합니다. C의 memmove()를 본땄다고 하네요. 목적지 인덱스, 시작 인덱스(없으면 0), 개수 인덱스(없으면 배열 길이) 순으로 인자를 받아요:
let a = [1, 2, 3, 4, 5];
console.log(a.copyWithin(1));
console.log(a.copyWithin(2, 3));
console.log(a.copyWithin(0, -2));
Array Searching and Sorting Methods
let a = [1, 2, 3, 2, 1];
console.log(a.indexOf(2));
console.log(a.lastIndexOf(2));
console.log(a.indexOf(4));
indexOf의 두번째 인자는 탐색을 시작할 인덱스를 의미합니다:
let findAll = (a, x) => {
let results = [];
let pos = 0;
while (pos < a.length) {
pos = a.indexOf(x, pos);
if (pos === -1) break;
results.push(pos++);
}
return results;
};
console.log(findAll([1, 2, 3, 2, 1], 2));
includes 메서드는 NaN 처리를 합니다:
let a = [1, NaN, 3];
console.log(a.indexOf(NaN));
console.log(a.includes(NaN));
sort
는 요소를 문자열로 바꿔서 사전순으로 정렬합니다. 대신 undefined는
마지막으로 간대요:
console.log([undefined, 9, 10, '200'].sort());
console.log([33, 4, 1111, 222].sort());
console.log([33, 4, 1111, 222].sort((a, b) => a - b));
Array to String Conversions
Array 객체의 toString은 인자 없는 join처럼 동작합니다:
console.log([1, 2, 3].toString());
console.log([, , ,].toString());
let a = { toLocaleString: () => 'aaa' };
let b = { toLocaleString: () => 'bbb' };
console.log([a, b].toString());
console.log([a, b].toLocaleString());
Static Array Functions
Array.isArray()는 프로토타입 체인에 Array.prototype이 있지만 실제 배열이 아닌 객체를 거부합니다. instanceof Array는 이를 허용합니다.
developer.mozilla.orgArray.isArray() - JavaScript | MDNArray.isArray() 정적 메서드는 전달된 값이 Array인지 판단합니다.
Determining with absolute accuracy whether or not a JavaScript object is an array
Array-Like Objects
배열 관련된 많은 알고리즘들이 객체가 length 프로퍼티와 0 이상 정수 프로퍼티들을 가지기만해도 잘 동작합니다:
let a = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
console.log(Array.prototype.join.call(a, '+'));
Client-side JS에서 많은 HTML document 메서드들이 유사 배열 객체를 반환합니다.
Strings as Arrays
let a = 'Javascript';
console.log(Array.prototype.join.call(a, ' '));
// string은 immutable합니다
console.log(Array.prototype.push.call(a, '!'));