27장 배열 정리
https://github.com/kheeyaa/TIL/tree/main/javascript/deepdive/Array
GitHub - kheeyaa/TIL: Today I Learned..
Today I Learned.. Contribute to kheeyaa/TIL development by creating an account on GitHub.
github.com
Array
// Array
// 배열: 여러 개의 값을 순차적으로 나열한 자료구조
// 요소: 배열이 가지고 있는 값
// 인덱스: 자신의 위치를 나타내는 0 이상의 정수
// length 프로퍼티: 배열의 길이
// 자바스크립트 배열 특징 ------------------------------------------------------------------
// - 밀집 배열 dense array
// 배열의 요소는 하나의 데이터 타입으로 통일되어 있고, 서로 연속적으로 인접해 있다.
// - 희소 배열
// 각각의 메모리 공간은 동일한 크기를 갖지 않아도 되고, 연속적으로 이어져있지 않아도 된다.
// 희소배열의 length는 희소 배열의 실제 요소 개수보다 언제나 크다
// 배열 생성 ------------------------------------------------------------------
// 1. 배열 리터럴
{
const arr1 = [1, 2, 3];
console.log(arr1.length); // 3
const arr2 = [];
console.log(arr2.length); // 0
const arr3 = [1, , 3]; // 희소배열
console.log(arr3.length); // 3
console.log(arr3); // [ 1, <1 empty item>, 3 ]
console.log(arr3[1]); // undefined
}
// 2. Array 생성자 함수
{
// 2-1. 전잘된 인수가 1개이고 숫자인 경우 => length 프로퍼티 값이 인수인 배열 생성
const arr1 = new Array(10);
console.log(arr1); // [ <10 empty items> ] => 희소 배열
console.log(arr1.length); // 10
// 2-2. 빈 배열
const arr2 = new Array(); // []
// 2-3. 인수가 2개 이상이거나, 숫자가 아닌 경우 => 인수를 요소로 갖는 배열 생성
const arr3 = new Array(1, 2, 3); // [1,2,3]
const arr4 = new Array({}); // [{}]
}
// 3. Array.of
{
// 전달된 인수를 요소로 갖는 배열 생성
// 전달된 인수가 1개고 숫자여도 인수를 요소로 가진다.
const arr1 = Array.of(1); // [1]
const arr2 = Array.of(1, 2, 3); // [1,2,3]
const arr3 = Array.of('string'); // ['string']
}
// 4. Array.from 메서드
{
// 인수로 유사 배열 객체 또는 이터러블 객체를 받아 배열로 변환하여 반환함
// 4-1. 유사 배열 객체
const arr1 = Array.from({ length: 2, 0: 'a', 1: 'b' }); // ['a', 'b']
// 4-2. 이터러블
const arr2 = Array.from('Hello'); // [ 'H', 'e', 'l', 'l', 'o' ]
// 4-3. 두 번째 인수로 전달한 콜백함수의 반환값으로 구성된 배열을 반환함
const arr3 = Array.from({ length: 3 }); // [ undefined, undefined, undefined ]
const arr4 = Array.from({ length: 3 }, (_, i) => i); // [ 0, 1, 2 ]
}
// 배열 참조 ------------------------------------------------------------------
{
// 존재하지 않는 요소에 접근하면 undefined
// 존재하지 않는 프로퍼티 키로 객체의 프로퍼티에 접근했을 때 undefined를 반환하는 것처럼
const arr = [1, 2];
console.log(arr[2]); // undefined
}
// 배열 요소 추가 갱신 ------------------------------------------------------------------
{
// 1. 요소 추가시 length 자동 갱신
const arr = [0];
arr[1] = 1;
console.log(arr.length); // 2
// 2. length보다 큰 인덱스로 새로운 요소를 추가하면 희소배열이 된다
arr[100] = 100;
console.log(arr); // [ 0, 1, <98 empty items>, 100 ]
console.log(arr.length); // 101
console.log(Object.getOwnPropertyDescriptors(arr));
/*
{
'0': { value: 0, writable: true, enumerable: true, configurable: true },
'1': { value: 1, writable: true, enumerable: true, configurable: true },
'100': { value: 100, writable: true, enumerable: true, configurable: true },
length: {
value: 101,
writable: true,
enumerable: false,
configurable: false
}
}
*/
// 3. 요소값 갱신
arr[1] = 10;
console.log(arr); // [ 0, 10, <98 empty items>, 100 ]
}
// 배열 요소 삭제 ------------------------------------------------------------------
{
// 1. delete 연산자
// 프로퍼티 키가 '1'인 프로퍼티를 삭제하고 희소 배열이 된다. 쓰지말자!
const arr1 = [1, 2, 3];
delete arr1[1];
console.log(arr1); // [ 1, <1 empty item>, 3 ]
console.log(arr1.length); // 3 => 희소 배열
// 2. Array.prototype.splice(삭제 시작 인덱스, 삭제할 요소 수) 메서드
const arr2 = [1, 2, 3];
arr2.splice(1, 1);
console.log(arr2); // [1, 3]
console.log(arr2.length); // 2
}
Array Method
// Array method
// mutator method: 원본 배열을 직접 변경하는 메서드
// accessor method: 원본 배열을 직접 변경하지 않고 새로운 배열을 생성하여 반환하는 메서드
// 가급적 원본 배열을 직접 변경하지 않는 메서드를 사용하자!
// Array.isArray ------------------------------------------------------------------
{
// 전달된 인수가 배열이면 true, 배열이 아니면 false
// 1. true
Array.isArray([]);
Array.isArray([1, 2]);
Array.isArray(new Array());
// 2. false
Array.isArray();
Array.isArray({});
Array.isArray(null);
Array.isArray(undefined);
Array.isArray(1);
Array.isArray('Array');
Array.isArray(true);
Array.isArray(false);
Array.isArray({ 0: 1, length: 1 });
}
// Array.prototype.indexOf ------------------------------------------------------------------
{
// 원본 배열에서 인수로 전달된 요소를 검색하여 인덱스를 반환함
// 1. 중복되는 요소가 여러 개 있다면 첫 번째로 검색된 요소의 인덱스를 반환
// 2. 존재하지 않으면 -1
// 3. 두번째 인수는 검색을 시작할 인덱스. 두번째 인수를 생략하면 처음부터 검색함
// NaN이 포함되어있는지 확인 불가
const arr = [1, 2, 2, 3];
console.log(arr.indexOf(2)); // 1
console.log(arr.indexOf(4)); // -1
console.log(arr.indexOf(2, 2)); // 2
// 배열에 특정 요소가 있는지 확인할 때 유용하다
// ES7 에 도입된 Array.prototype.include 메서드가 가독성이 더 좋다
}
// Array.prototype.push ------------------------------------------------------------------
{
// 인수로 전달받은 모든 값을 원본 배열의 마지막 요소로 추가
// 변경된 length 프로퍼티 값을 반환함
// 원본 배열을 직접 변경 => 부수 효과 => 스프레드 문법을 사용하자 [...arr, 3, 4]
const arr = [1, 2];
let result = arr.push(3, 4);
console.log(arr); // [ 1, 2, 3, 4 ]
console.log(result); // 4
// push 메서드는 성능이 좋지 않다.
// 마지막 요소로 추가할 요소가 하나 뿐이면 length 프로퍼티를 사용해 직접 추가하는 편이 더 빠르다.
arr[arr.length] = 5;
console.log(arr); // [ 1, 2, 3, 4, 5 ]
}
// Array.prototype.pop ------------------------------------------------------------------
{
// 원본 배열에서 마지막 요소를 제거하고 제거한 요소를 반환
// 빈 배열인 경우 undefined를 반환
// 원본 배열을 직접 변경
const arr = [1, 2];
let result = arr.pop(); // 2
}
// Array.prototype.unshift ------------------------------------------------------------------
{
// 인수로 전달 받은 모든 값을 원본 배열의 선두에 요소로 추가
// 변경된 length 프로퍼티 값을 반환
// 원본 배열을 직접 변겅
const arr = [1, 2];
let result = arr.unshift(3, 4); // 4
console.log(arr); // [ 3, 4, 1, 2 ]
// 부수효과가 있으니 ES6 스프레드 문법을 사용하자
const newArr = [5, ...arr];
console.log(newArr); // [ 5, 3, 4, 1, 2 ]
}
// Array.prototype.shift ------------------------------------------------------------------
{
// 원본 배열에서 첫 번째 요소를 제거함
// 제거한 요소를반환
// 빈 배열인 경우 undefined를 반환
// 원본 배열 직접 변경
const arr = [1, 2];
console.log(arr.shift()); // 1
console.log(arr); // [2]
}
// Array.prototype.concat ------------------------------------------------------------------
{
// 인수로 전달된 값들(배열 또는 원시값)을 원본 배열의 마지막 요소로 추가한 새로운 배열을 반환
// 인수로 전달한 값이 배열인 경우 배열을 해체하여 새로운 배열의 요소로 추가
// 원본 배열은 변경되지 않음
const arr1 = [1, 2];
const arr2 = [3, 4];
console.log(arr1.concat(arr2)); // [ 1, 2, 3, 4 ]
console.log(arr1.concat(3)); // [ 1, 2, 3 ]
console.log(arr1.concat(arr2, 5)); // [ 1, 2, 3, 4, 5 ]
console.log(arr1); // [ 1, 2 ]
// unshift와 push 메서드는 인수로 전달 받은 배열을 그대로 원본 배열의 요소로 추가한다.
const arr = [3, 4];
arr.unshift([1, 2]);
arr.push([5, 6]);
console.log(arr); // [ [ 1, 2 ], 3, 4, [ 5, 6 ] ]
// concat 메서드는 인수로 전달받은 배열을 해체하여 새로운 배열의 요소로 추가
let result = [1, 2].concat([3, 4]);
result = result.concat([5, 6]);
console.log(result); // [ 1, 2, 3, 4, 5, 6 ]
// concat은 ES6의 스프레드 문법으로 대체 가능
let result2 = [...[1, 2], ...[3, 4]];
console.log(result2); // [ 1, 2, 3, 4 ]
}
// Array.prototype.splice ------------------------------------------------------------------
{
// 원본 배열의 중간에 요소를 추가하거나 제거할 때
// 제거한 요소가 배열로 반환됨
// 원본 배열을 직접 변경
// Array.prototype.splice(start, deleteCount, items)
// * start: 원본 배열의 요소를 제거하기 시작할 인덱스
// => -1 : 마지막 인덱스, -n: 마지막에서 n번째 요소
// * deleteCount: 제거할 요소 개수, 0이면 제거되지 않는다, 생략하면 start부터 이후 요소를 모두 삭제한다.
// * items: 제거한 위치에삽입할 요소들의 목록, 생략하면 요소들을 제거하기만 한다
const arr = [1, 2, 3, 4];
const result = arr.splice(1, 2, 20, 30);
console.log(result); // 제거한 요소: [ 2, 3 ]
console.log(arr); // 원본 변경: [ 1, 20, 30, 4 ]
}
// Array.prototype.slice ------------------------------------------------------------------
{
// 인수로 전달된 범위의 요소들을 복사하여 배열로 반환함
// 원본 배열은 변경되지 않음
// splice와 혼돈하지 말자
// Array.prototype.slice(start, end)
// * start: 복사를 시작할 인덱스
// => -1 : 마지막 인덱스, -n: 마지막에서 n번째 요소
// * end: 복사를 종료한 인덱스, 이 인덱스에 해당하는 요소는 복사되지 않는다. 생략하면 끝까지 복사한다.
// 인수를 모두 생략하면 원본 배열의 복사본을 생성하여 반환함 (얕은 복사. lv1 까지만 복사함.)
const arr = [1, 2, 3];
console.log(arr.slice(0, 1)); // [1]
console.log(arr.slice(1, 2)); // [2]
console.log(arr.slice(1)); // [2, 3]
console.log(arr.slice(-2)); // [2, 3] : 끝에서부터 두 개 복사
console.log(arr.slice()); // [ 1, 2, 3 ] : 원본 배열 복사
console.log(arr); // [ 1, 2, 3 ] => 원본 그대로
}
// Array.prototype.join ------------------------------------------------------------------
{
// 원본 배열의 모든 요소를 문자열로 변환한 후
// 인수로 받은 문자열, 즉 구분자로 연결한 문자열을 반환함.
// 원본은 변하지 않음
const arr = [1, 2, 3, 4];
// 기본은 콤마 (',')
console.log(arr.join()); // 1,2,3,4
console.log(arr.join('')); // 1234
console.log(arr.join(':')); // 1:2:3:4
console.log(arr); // [ 1, 2, 3, 4 ]
}
// Array.prototype.reserve ------------------------------------------------------------------
{
// 원본 배열의 순서를 반대로 뒤집음
// 원본 배열이 변경됨
// 반환값은 변경된 배열
const arr = [1, 2, 3];
const result = arr.reverse();
console.log(arr); // [ 3, 2, 1 ]
console.log(result); // [ 3, 2, 1 ]
}
// Array.prototype.fill ------------------------------------------------------------------
{
// 인수로 전달받은 값을 배열의 처음부터 끝까지 요소로 채운다.
// 원본 배열이 변경된다
const arr1 = [1, 2, 3];
arr1.fill(0);
console.log(arr1); // [ 0, 0, 0 ]
// 두 번째 인수로 요소를 채우기 시작할 인덱스를 전달할 수 있다.
const arr2 = [1, 2, 3, 4, 5];
arr2.fill(0, 1);
console.log(arr2); // [ 1, 0, 0, 0, 0 ]
// 세 번째 인수로 요소 채우기를 멈출 인덱스를 전달
const arr3 = [1, 2, 3, 4, 5];
arr3.fill(0, 1, 3);
console.log(arr3); // [ 1, 0, 0, 4, 5 ]
}
// Array.prototype.includes ------------------------------------------------------------------
{
// 배열 내에 특정 요소가 포함되어 있는지 확인하여 true 또는 false를 반환
// 첫 번째 인수로 검색할 대상 지정
// 두 번째 인수로 검색을 시작할 인덱스 전달, 음수라면 (legnth + index)로 검색 시작 인덱스 설정
// NaN이 포함되어 있는지 확인 가능
const arr = [1, 2, 3];
arr.includes(2); // true
arr.includes(100); // false
arr.includes(1, 1); // false
arr.includes(3, -1); // true
}
// Array.prototype.flat ------------------------------------------------------------------
{
// 인수로 전달한 깊이 만큼 재귀적으로 배열을 평탄화 한다.
console.log([1, [2, 3, 4, 5]].flat()); // [ 1, 2, 3, 4, 5 ]
// 깊이의 기본 값은 1
console.log([1, [2, [3, [4]]]].flat()); // [ 1, 2, [ 3, [ 4 ] ] ]
console.log([1, [2, [3, [4]]]].flat(1)); // [ 1, 2, [ 3, [ 4 ] ] ]
// 중첩 배열 모두 평탄화 Infinity
console.log([1, [2, [3, [4]]]].flat(Infinity)); // [ 1, 2, 3, 4]
}
Array Higher Order Function
// 배열 고차 함수 Array Higher-Order Function
// 함수를 인수로 전달 받거나 함수를 반환하는 함수를 말함.
// 고차 함수는 외부 상태의 변경이나 가변 데이터를 피하고 불변성을 지향하는 함수형 프로그래밍에 기반을 두고있다.
// 함수형 프로그래밍
// - 순수 함수와 보조 함수의 조합으로 로직 내 조건문과 반복문을 제거하여 복잡성을 해결
// - 변수의 사용을 억제하여 상태 변경을 피함
// 순수 함수를 통해 부수 효과를 최대한 억제하여 오류를 피하고 프로그맹의 안정성을 높이려는 노력의 일환임.
// Array.prototype.sort ------------------------------------------------------------------
{
// 배열의 요소를 정렬함
// 원본 배열을 직접 변경
// 정렬된 배열을 반환함
// 1. 기본 오름차순
const fruits = ['Banana', 'Orange', 'Apple'];
fruits.sort();
console.log(fruits); // [ 'Apple', 'Banana', 'Orange' ]
// 2. 한글 문자열도 오름차순
const fruits2 = ['바나나', '오렌지', '사과'];
fruits2.sort();
console.log(fruits2); // [ '바나나', '사과', '오렌지' ]
// 3. 내림차순 정렬
// sort로 오름차순 한 다음 reverse로 순서 뒤집기
console.log(fruits); // [ 'Apple', 'Banana', 'Orange' ]
fruits.reverse();
console.log(fruits); // [ 'Orange', 'Banana', 'Apple' ]
// 4. 숫자 요소로 이루어진 배열 정렬
const points = [40, 100, 1, 5, 2, 25, 10];
points.sort();
// 4-1. 숫자 요소들로 이뤄진 배열은 의도한 대로 정렬되지 않는다.
console.log(points); // [1, 10, 100, 2, 25, 40, 5]
// 배열의 요소가 숫자 타입이면 일시적으로 문자열로 변환한 후 "유니코드" 코드 포인트의 순서를 기준으로 정렬하기 때문!
// 4-2. 숫자 요소를 정렬할 때는 sort 매서드에 <정렬 순서를 정의하는 비교 함수>를 인수로 전달해야함.
// 숫자 배열의 오름차순 정렬.
// 비교 함수의 반환값이 0보다 작으면 a를 우선하여 정렬한다.
points.sort((a, b) => a - b);
console.log(points); // [1, 2, 5, 10, 25, 40, 100]
// 숫자 배열의 내림차순 정렬.
// 비교 함수의 반환 값이 0보다 작으면 b를 우선하여 정렬한다.
points.sort((a, b) => b - a);
console.log(points); // [100, 40, 25, 10, 5, 2, 1]
// 5. 객체를 요소로 갖는 배열을 정렬
const todos = [
{ id: 4, content: 'JavaScript' },
{ id: 1, content: 'HTML' },
{ id: 2, content: 'CSS' },
];
// 5-1. 비교함수, 매개변수 key는 프로퍼티 키
function compare(key) {
// 프로퍼티 값이 문자열인 경우 - 산술 연산을 하면 NaN이 나올 수 있음. 그래서 비교 연산을 수행
// 비교 함수는 양수/음수/0을 반환하면 됨
// 오름차순 정렬
// 양수,0: 그대로 / 음수: a를 기준으로 정렬
return (a, b) => (a[key] > b[key] ? 1 : a[key] < b[key] ? -1 : 0);
}
// 5-2. id를 기준으로 오름차순 정렬
todos.sort(compare('id'));
console.log(todos);
/*
[
{ id: 1, content: 'HTML' },
{ id: 2, content: 'CSS' },
{ id: 4, content: 'JavaScript' }
]*/
// 5-3. content를 기준으로 오름차순 정렬
todos.sort(compare('content'));
console.log(todos);
/*
[
{ id: 2, content: 'CSS' },
{ id: 1, content: 'HTML' },
{ id: 4, content: 'JavaScript' }
]
*/
}
// Array.prototype.forEach ------------------------------------------------------------------
{
// 내부 반복문을 통해 자신을 호출한 배열을 순회하며 수행해야 할 처리를 콜백 함수로 전달받아 반복 호출함
// 1. 콜백 함수의 인수 (1) 배열의 요소값 (2) 인덱스 (3) 호출한 배열 자체(this)
// 2. forEach는 원본 배열을 변경하지 않는다.
// 3. 반환값은 언제나 undefined
// 4. forEach 메서드의 두 번째 인수로 forEach 메서드 콜백 함수 내부에서 this로 사용할 객체를 전달할 수 있다
// 5. break, continue 문을 사용할 수 없다
// 6. 존재하지 않는 요소는 순회 대상에서 제외된다.
const numbers = [1, 2, 3];
const pows = [];
numbers.forEach(item => pows.push(item ** 2));
console.log(pows); // [ 1, 4, 9 ]
// 1. 콜백 함수의 인수
// (1) 배열의 요소값 (2) 인덱스 (3) 호출한 배열 자체(this)
[1, 2, 3].forEach((item, index, arr) => {
console.log(`요소값: ${item}, 인덱스: ${index}, this: ${JSON.stringify(arr)}`);
});
/*요소값: 1, 인덱스: 0, this: [1,2,3]
요소값: 2, 인덱스: 1, this: [1,2,3]
요소값: 3, 인덱스: 2, this: [1,2,3] */
// 2. forEach는 원본 배열을 변경하지 않는다.
// 3. 반환값은 언제나 undefined
// 4. forEach 메서드의 두 번째 인수로 forEach 메서드 콜백 함수 내부에서 this로 사용할 객체를 전달할 수 있다
{
class Numbers {
numberArray = [];
multiply(arr) {
arr.forEach(function (item) {
this.numberArray.push(item * item);
}, this); // forEach 메서드의 콜백 함수 내부에서 this로 사용할 객체를 전달
}
}
const numbers = new Numbers();
numbers.multiply([1, 2, 3]);
console.log(numbers.numberArray); // [1, 4, 9]
}
// 4-1. 화살표 함수
// 화살표 함수 내부에서 this를 참조하면 상위 스코프, 즉 multiply메서드 내부의 this를 그대로 참조함
{
class Numbers {
numberArray = [];
multiply(arr) {
arr.forEach(item => this.numberArray.push(item * item));
}
}
const numbers = new Numbers();
numbers.multiply([1, 2, 3]);
console.log(numbers.numberArray); // [1, 4, 9]
}
// 5. break, continue 문을 사용할 수 없다
// 6. 존재하지 않는 요소는 순회 대상에서 제외된다.
{
const arr = [1, , 3];
// for문
for (let i = 0; i < arr.length; i++) console.log(arr[i]); // 1, undefined, 3
// forEach
arr.forEach(v => console.log(v)); // 1, 3
}
}
// Array.prototype.map ------------------------------------------------------------------
{
// 자신을 호출한 배열의 모든 요소를 순회하면서 인수로 전달받은 콜백 함수를 반복 호출한다.
// "콜백 함수의 반환값"들로 구성된 새로운 배열을 반환한다.
// 원본 배열은 변경되지 않는다.
// 1. 원본 배열과 반환된 배열은 1:1 매핑된다
// 2. 콜백 함수의 인수 (1) 배열의 요소값 (2) 인덱스 (3) 호출한 배열 자체(this)
// 3. map 메서드의 두 번째 인수로 map 메서드 콜백 함수 내부에서 this로 사용할 객체를 전달할 수 있다
const numbers = [1, 4, 9];
const roots = numbers.map(item => Math.sqrt(item));
console.log(roots); // [ 1, 2, 3 ]
console.log(numbers); // [ 1, 4, 9 ]
// 1. 원본 배열과 반환된 배열은 1:1 매핑된다
// map 메서드가 생성하여 반환하는 새로운 배열의 length 프로퍼티 값은
// map 메서드를 호출한 배열의 length 프로퍼티 값과 반드시 일치함
// 즉, map 메서드를 호출한 배열과 map 메서드가 생성하여 반환한 배열은 1:1 매핑됨
// 2. 콜백 함수의 인수
// (1) 배열의 요소값 (2) 인덱스 (3) 호출한 배열 자체(this)
[1, 2, 3].map((item, index, arr) => {
console.log(`요소값: ${item}, 인덱스: ${index}, this: ${JSON.stringify(arr)}`);
});
/*요소값: 1, 인덱스: 0, this: [1,2,3]
요소값: 2, 인덱스: 1, this: [1,2,3]
요소값: 3, 인덱스: 2, this: [1,2,3] */
// 3. map 메서드의 두 번째 인수로 map 메서드 콜백 함수 내부에서 this로 사용할 객체를 전달할 수 있다
{
class Prefixer {
constructor(prefix) {
this.prefix = prefix;
}
add(arr) {
return arr.map(function (item) {
// 외부에서 this를 전달하지 않으면 this는 undefined를 가리킨다.
return this.prefix + item;
}, this); // map 메서드의 콜백 함수 내부에서 this로 사용할 객체를 전달
}
}
const prefixer = new Prefixer('-webkit-');
console.log(prefixer.add(['transition', 'user-select']));
// ['-webkit-transition', '-webkit-user-select']
}
// 3-1. 화살표 함수가 더 좋다
{
class Prefixer {
constructor(prefix) {
this.prefix = prefix;
}
add(arr) {
// 화살표 함수 내부에서 this를 참조하면 상위 스코프의 this를 그대로 참조한다.
return arr.map(item => this.prefix + item);
}
}
const prefixer = new Prefixer('-webkit-');
console.log(prefixer.add(['transition', 'user-select']));
// ['-webkit-transition', '-webkit-user-select']
}
}
// Array.prototype.filter ------------------------------------------------------------------
{
// 자신을 호출한 배열의 모든 요소를 순회하며 인수로 전달받은 콜백 함수를 반복 호출, 원본 배열을 바꾸지 않는다.
// 1. 콜백 함수의 반환값이 true인 요소로만 구성된 새로운 배열을 반환
// 2. 콜백 함수의 인수 (1) 배열의 요소값 (2) 인덱스 (3) 호출한 배열 자체(this)
// 3. filter 메서드의 두 번째 인수로 filter 메서드 콜백 함수 내부에서 this로 사용할 객체를 전달할 수 있다
// 1. 콜백 함수의 반환값이 true인 요소로만 구성된 새로운 배열을 반환
// filter 메서드가 생성하여 반환한 새로운 배열의 length 프로퍼티 값은
// filter 메서드를 호출한 배열의 length 프로퍼티 값과 같거나 작다.
{
const numbers = [1, 2, 3, 4, 5];
const odds = numbers.filter(item => item % 2);
console.log(odds); // [ 1, 3, 5 ]
}
// forEach: 반환값은 undfined
// map: 콜백함수의 반환값들로 구성된 배열을 새로 반환
// filter: 콜백함수의 반환값이 true인 요소만 추출하여 새로운 배열을 반환
// 2. 콜백 함수의 인수 (1) 배열의 요소값 (2) 인덱스 (3) 호출한 배열 자체(this)
[1, 2, 3].filter((item, index, arr) => {
console.log(`요소값: ${item}, 인덱스: ${index}, this: ${JSON.stringify(arr)}`);
return item % 2;
});
/*
요소값: 1, 인덱스: 0, this: [1,2,3]
요소값: 2, 인덱스: 1, this: [1,2,3]
요소값: 3, 인덱스: 2, this: [1,2,3]
*/
// 3. filter 메서드의 두 번째 인수로 filter 메서드 콜백 함수 내부에서 this로 사용할 객체를 전달할 수 있다
// filter 메서드는 자신이 호출한 배열에서 특정 요소를 제거하기 위해 사용할 수 있음
{
class Users {
constructor() {
this.users = [
{ id: 1, name: 'Lee' },
{ id: 2, name: 'Kim' },
];
}
// 요소 추출
findById(id) {
// id가 일치하는 사용자만 반환한다.
return this.users.filter(user => user.id === id);
}
// 요소 제거
remove(id) {
// id가 일치하는 사용자를 제거한다.
this.users = this.users.filter(user => user.id !== id);
}
}
const users = new Users();
let user = users.findById(1);
console.log(user); // [{ id: 1, name: 'Lee' }]
// id가 1인 사용자를 제거한다.
users.remove(1);
user = users.findById(1);
console.log(user); // []
}
}
// Array.prototype.reduce ------------------------------------------------------------------
{
// 자신을 호출한 배열의 모든 요소를 순회하며 인수로 전달받은 콜백 함수를 반복 호출, 원본 배열을 바꾸지 않는다.
// 1. 콜백 함수의 반환값을 다음 순회 시 콜백 함수의 첫 번째 인수로 전달하면서
// 콜백 함수를 호출하여 <하나의 결과값>을 만들어 반환한다.
// 2. reduce의 인수 (1) 콜백 함수 (2) 초기값 : 생략은 가능하지만 안전하지 않다.
// 3. 콜백 함수의 인수 (1) 초기값 또는 함수의 이전 반환값 (2) 배열의 요소값 (3) 인덱스 (4) 호출한 배열 자체(this)
// 누적 합 구하기
{
const sum = [1, 2, 3, 4].reduce(
(accumulator, currentValue, index, array) => accumulator + currentValue,
0
);
console.log(sum); // 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); // 3.5
}
// 최대값 구하기
{
const values = [1, 2, 3, 4, 5];
const max = values.reduce((acc, cur) => (acc > cur ? acc : cur), 0);
console.log(max); // 5
}
// 요소의 중복 횟수 구하기
{
const fruits = ['banana', 'apple', 'orange', 'orange', 'apple'];
const count = fruits.reduce((acc, cur) => {
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 }
}
// 중첩 배열 평탄화
{
const values = [1, [2, 3], 4, [5, 6]];
const flatten = values.reduce((acc, cur) => acc.concat(cur), []);
console.log(flatten); // [ 1, 2, 3, 4, 5, 6 ]
}
// 중복 요소 제거
{
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 ]
}
}
// Array.prototype.some ------------------------------------------------------------------
{
// 자신을 호출한 배열의 모든 요소를 순회하며 인수로 전달받은 콜백 함수를 반복 호출, 원본 배열을 바꾸지 않는다.
// 배열의 요소 중에 콜백 함수를 통해 정의한 조건을 만족하는 요소가 1개 이상 존재하는지 그 결과를 불리언 타입으로 반환한다.
// 1. 콜백 함수의 반환값이 단 한 번이라도 참이면 treu, 모두 거짓이면 false, 빈 배열인 경우 언제나 false
// 2. 콜백 함수의 인수 (1) 배열의 요소값 (2) 인덱스 (3) 호출한 배열 자체(this)
// 3. some 메서드의 두 번째 인수로 some 메서드 콜백 함수 내부에서 this로 사용할 객체를 전달할 수 있다
[5, 10, 15].some(item => item > 10); // true
[5, 10, 15].some(item => item < 0); // false
['apple', 'banana', 'mango'].some(item => item === 'banana'); // true
[].some(item => item > 3); // false
}
// Array.prototype.every ------------------------------------------------------------------
{
// 자신을 호출한 배열의 모든 요소를 순회하며 인수로 전달받은 콜백 함수를 반복 호출, 원본 배열을 바꾸지 않는다.
// 배열의 요소 중에 콜백 함수를 통해 정의한 조건을 모두 만족하는지 확인하여 그 결과를 불리언 타입으로 반환한다.
// 1. 콜백 함수의 반환값이 모두 참이면 true, 단 한 번이라도 거짓이면 false, 빈 배열인 경우 언제나 true
// 2. 콜백 함수의 인수 (1) 배열의 요소값 (2) 인덱스 (3) 호출한 배열 자체(this)
// 3. every 메서드의 두 번째 인수로 every 메서드 콜백 함수 내부에서 this로 사용할 객체를 전달할 수 있다
[5, 10, 15].some(item => item > 3); // true
[5, 10, 15].some(item => item > 10); // false
[].some(item => item > 3); // true
}
// Array.prototype.find ------------------------------------------------------------------
{
// 자신을 호출한 배열의 모든 요소를 순회하며 인수로 전달받은 콜백 함수를 반복 호출, 원본 배열을 바꾸지 않는다.
// 1. 콜백 함수의 반환값이 true인 첫 번째 요소를 반환한다. 콜백 함수의 반환값이 true인 요소가 없다면 undefined를 반환한다.
// 2. 콜백 함수의 인수 (1) 배열의 요소값 (2) 인덱스 (3) 호출한 배열 자체(this)
// 3. find 메서드의 두 번째 인수로 find 메서드 콜백 함수 내부에서 this로 사용할 객체를 전달할 수 있다
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'}
// filter 메서드는 배열을 반환한다.
[1, 2, 2, 3].filter(item => item === 2); // [2, 2]
// find 메서드는 요소를 반환한다.
[1, 2, 2, 3].find(item => item === 2); // 2
}
// Array.prototype.findIndex ------------------------------------------------------------------
{
// 자신을 호출한 배열의 모든 요소를 순회하며 인수로 전달받은 콜백 함수를 반복 호출, 원본 배열을 바꾸지 않는다.
// 1. 콜백 함수의 반환값이 true인 첫 번째 요소의 인덱스를 반환한다. 콜백 함수의 반환값이 true인 요소가 없다면 -1를 반환한다.
// 2. 콜백 함수의 인수 (1) 배열의 요소값 (2) 인덱스 (3) 호출한 배열 자체(this)
// 3. findIndex 메서드의 두 번째 인수로 findIndex 메서드 콜백 함수 내부에서 this로 사용할 객체를 전달할 수 있다
const users = [
{ id: 1, name: 'Lee' },
{ id: 2, name: 'Kim' },
{ id: 2, name: 'Choi' },
{ id: 3, 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
}
// Array.prototype.flatMap ------------------------------------------------------------------
{
// flatMap 메서드는 map 메서드를 통해 생성된 새로운 배열을 평탄화한다.
// 즉 map 메서드와 flat 메서드를 순차적으로 실행하는 효과가 있다.
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 메서드를 통해 생성된 중첩 배열의 평탄화 깊이를 지정해야 하면 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]
}
'JavaScript > JavaScript 예복습' 카테고리의 다른 글
JavaScript 예습 | 19~21장 (0) | 2021.09.16 |
---|---|
JavaScript 예습 | 16~18장 (0) | 2021.09.14 |
JavaScript 예습 | [deep dive] 13 ~ 15장 (0) | 2021.09.12 |
JavaScript 예습 | [deep dive] 10 ~ 12장 (0) | 2021.09.09 |
JavaScript 예습 | [deep dive] 6장~9장 (0) | 2021.09.07 |