0%

201221_TIL(배열)

오늘 배운 것

배열 고차 함수

메서드 체이닝을 사용할 때는 메서드가 this를 반환하는 뮤테이터(mutator) 메서드여야한다.

1
2
3
4
5
const fruits = ["Banana", "Orange", "Apple"];

fruits.sort().reverse();

console.log(fruits); // ['Orange', 'Banana', 'Apple']

Array.prototype.sort

객체를 요소로 갖는 배열을 정렬하는 예제는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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' }
]
*/

forEach

1
2
3
4
5
6
7
8
9
10
11
12
13
const todos = [
{ id: 4, content: "JavaScript" },
{ id: 1, content: "HTML" },
{ id: 2, content: "CSS" },
];

let html = "";

todos.forEach((todo) => {
html += `<li id = "${todo.id}"> ${todo.content} </li>\n`;
});

console.log(html);

reduce

콜백 함수의 반환값을 다음 순회 시에 콜백 함수의 첫 번째 인수로 전달하면서 콜백 함수를 호출하여 하나의 결과값을 만들어 반환한다.

1
2
3
4
5
6
7
// [1, 2, 3, 4]의 모든 요소의 누적을 구한다.
const sum = [1, 2, 3, 4].reduce(
(accumulator, currentValue, index, array) => accumulator + currentValue,
0
);

console.log(sum); // 10

reduce 메서드는 2개의 인수, 즉 콜백 함수와 초기값 0을 전달받아 자신을 호출한 배열의 모든 요소를 누적한 결과를 반환한다.

reduce 메서드의 콜백 함수는 4개의 인수를 전달받아 배열의 length만큼 총 4회 호출된다. 이때 콜백 함수로 전달되는 인수와 콜백 함수의 반환값은 다음과 같다.

요소의 중복 횟수 구하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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); // { banana: 1, apple: 2, orange: 2 }

중복 요소 제거

1
2
3
4
5
6
7
8
9
10
11
const values = [1, 2, 1, 3, 5, 4, 5, 3, 4, 4];

const result = values.reduce((acc, cur, i, arr) => {
// 순회 중인 요소의 인덱스가 자신의 인덱스라면 처음 순회하는 요소다.
// 이 요소만 초기값으로 전달받은 배열에 담아 반환한다.
// 순회 중인 요소의 인덱스가 자신의 인덱스가 아니라면 중복된 요소다.
if (arr.indexOf(cur) === i) acc.push(cur);
return acc;
}, []);

console.log(result); // [1, 2, 3, 5, 4]

reduce 메서드를 호출할 때는 초기값을 생략하지 말고 언제나 전달하는 것이 안전하다.

flatMap

1
2
3
4
5
6
7
8
9
10
11
12
const arr = ["hello", "world"];

arr.map((x) => x.split(""));
// -> [ [ 'h', 'e', 'l', 'l', 'o' ], [ 'w', 'o', 'r', 'l', 'd' ] ]

// 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단계만 평탄화한다.

Number.EPSILON

1
2
0.1 + 0.2; // -> 0.30000000000000004
0.1 + 0.2 === 0.3; // -> false

위 결과는 실수를 이진수로 변환할 때 생기는 바이트 손상 때문에 정확한 계산이 안되며 이는 컴퓨터의 구조적 한계이다.

1 에서 (JS에서 표현 가능한 1 다음으로 큰 숫자 예를들어 1.00000000001)를 빼기 연산했을 때의 값이 EPSILON이며 이는 숫자와 다음 숫자 사이의 간극이다.

0.300000000004 - 0.3이 EPSILON보다 작다면 같은 수로 인정한다.

1
2
3
4
5
6
function isEqual(a, b) {
// a와 b를 뺀 값의 절대값이 Number.EPSILON보다 작으면 같은 수로 인정한다.
return Math.abs(a - b) < Number.EPSILON;
}

isEqual(0.1 + 0.2, 0.3); // -> true

Nyong’s GitHub