배열메서드, 배열고차함수
by EunHye Jung
배열메서드
- 자바스크립트 배열 메서드는 결과물을 반환하는 패턴이 두가지가 있다.
1) 원본 배열(배열 메서드를 호출한 배열)을 직접 변경하는 메서드(mutator method)
2) 원본 배열을 직접 변경하지 않고 새로운 배열을 생성하여 반환하는 메서드(accessor method)
ex)const arr = [1]; arr.push(2); // push 메서드는 원본 배열(arr)을 직접 변경함 console.log(arr); // [1, 2] const result = arr.concat(3); // concat 메서드는 원본 배열(arr)을 직접 변경하지 않고 새로운 배열을 생성하여 반환 console.log(arr); // [1, 2] console.log(result); // [1, 2, 3]
- ES5부터 도입된 배열 메서드는 대부분 원본 배열을 직접 변경하지 않지만, 초창기 배열메서드는 원본 배열을 직접 변경하는 경우가 많음.
가급적 원본 배열을 직접 변경하지 않은 메서드(accessor method)를 사용하는편이 좋음.
배열고차함수
- 고차함수(higher-order function)는 함수를 인수로 전달받거나 함수를 반환하는 함수를 말함.
- 고차함수는 외부함수의 변경이나 가변데이터를 피하고 불변성을 지향하는 함수형프로그래밍에 기반을 두고 있다.
Array.prototype.sort
- sort 메서드는 배열요소를 정렬, 원본 배열을 직접 변경하여 정렬된 배열을 반환함.
- sort메서드는 기본적으로 오름차순으로 정렬되며, 내림차순 정렬하려면 sort 메서드 사용 후 reverse 메서드를 사용.
- sort 메서드 사용시 문자열 정렬은 문제가 없지만 숫자로 이루어진 배열을 정렬할땐 주의가 필요함.
sort 메서드의 기본 정렬 순서는 유니코드 코드 포인트의 순서를 따름.
배열의 요소가 숫자타입이여도 배열의 요소를 일시적으로 문자열로 변환한 후 유니코드 코드 포인트의 순서를 기준으로 저열ㄹ함.
그래서 의도와는 다른 결과값을 얻을 수 있음.
ex)const points = [ 40, 100, 1, 5, 2, 25, 10 ]; points.sort(); console.log(points); // [ 1, 10, 100, 2, 25, 40, 5]
따라서 숫자요소를 정렬할 때 sort메서드에 정렬 순서를 정의하는 비교함수를 인수로 전달해야함.
비교함수의 반환값이 0보다 작으면 비교함수의 첫번쨰 인수를 우선정렬하고, 0이면 정렬하지 않으며, 0보다 크면 두번째 인수를 우선하여 정렬함.
ex)const points = [ 40, 100, 1, 5, 2, 25, 10]; poinst.sort((a, b) => a - b); console.log(points); // [ 1, 2, 5, 10, 25, 40, 100 ]
- 객체를 요소로 갖는 배열 정렬은 다음과 같이 진행.
ex)const todos = [ { id: 4, content: 'JavaScript'}, { id: 1, content: 'HTML' }, { id: 2, content: 'CSS' } ]; // 비교함수, 매개변수 key는 프로퍼티 키 function compare(key) { // 프로퍼티값이 문자열일 경우 - 산술연산으로 비교하면 NaN이 나오므로 비교연산을 사용함. 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'} ] */
cf) sort 메서드는 quicksort 알고리즘을 사용했었다. quicksort 알고리즘은 동일한 값의 요소가 중복되어 있을 때 초기 순서와 변경될 수 있는 불안정한 정렬알고리즘으로 알려져있음. ECMAScript2019(ES10)에서는 timesort알고리즘을 사용하도록 변경됨.
Array.prototype.forEach.
- forEach 메서드는 for문을 대체할 수 있는 고차함수.
- forEach 메서드는 자신의 내부 반복문을 반복적으로 실행함!
-> 반복문을 추상화한 고차함수로서 내부에서 반복문을 통해 자신을 호출한 배열을 순회화면서 수행해야할 처리를 콜백함수로 전달받아 반복호출함.
ex)const number = [ 1, 2, 3 ]; const pow = []; // forEach 메서드는 numbers 배열의 모든 요소를 순회하면서 콜백함수를 반복호출한다. number.forEach(item => pows.push(item **2)); console.log(pow); // [1, 4, 9]
- forEach 메서드의 콜백함수는 forEach 메서드를 호출한 배열의 요소값과 인덱스, forEach 메서드를 호출한 배열자체(this)를 순차적으로 전달받을 수 있다.
(여기서 말하는 this는 forEach 메서드 내부의 this를 의미, forEach메서드의 콜백함수 내부의 this를 의미지하지 않음에 주의!)// forEach 메서드는 콜백함수를 호출하면서 3개의 인수(요소값, 인덱스, this)를 전달한다. [1, 2, 3].forEach((item, index, arr) => { console.log(`요소값 : ${item}, 인덱스 : ${index}, this : ${JSON.stringify(arr)}`); });
- forEach 메서드는 원본배열을 변경하지 않지만 콜백함수를 통해 원본 배열을 변경할 수는 있다.
- forEach 메서드의 반환값은 언제나 undefined이다.
- forEach 메서드의 두번째 인수로 forEach 메서드의 콜백함수 내부에서 this로 사용할 객체를 전달할 수 있다.
class Number = { numberArray = []; multiplay(arr) { arr.forEach(function(item) { this.numberArray.push(item * item); }, this); // forEach 메서드의 콜백함수 내부에서 this로 사용할 객체를 전달 } const numbers = new Numbers(); numbers.multipy([1, 2, 3]); console.log(numbers.numberArray); // [1, 4, 9] }
더나은 방법은 ES6의 화살표 함수를 사용하는 것.
화살표함수는 함수자체의 this바인딩을 갖지않으므로 화살표함수 내부에서 this를 참조하면
상위스코프, 즉 multiply 메서드 내부의 this를 그대로 참조함.class Numbers { numberArray = []; multiply(arr) { // 화살표 함수 내부에서 this를 참조하면 상위스코프의 this를 그대로 참조함 arr.forEach(item => this.numberArray.push(item * item)); } } ...
- forEach 메서드는 for문과 달리 break, continue문을 사용할 수 없다.
즉, 배열의 모든요소를 순회하며 중간에 중단불가능. - 희소배열의 경우 존재하지 않는 요소는 순회대상에서 제외된다.
// 희소배열 const arr = [1, , 3]; for(let i = 0; i < arr.length; i++) { console.log(arr[i]); // 1, undefined, 3 } // forEach 메서드는 희소배열의 존재하지 않는 요소를 순회대상에서 제외한다. arr.forEach(v => console.log(v)); // 1, 3
- forEach 메서드는 for문에 비해 성능은 좋지않지만 가독성은 더 좋다.
따라서 요소가 엄청 많은 배열을 순회하거나 시간이 많이걸리는 복잡한 코드 또는 높은 성능이 필요한 경우가 아니라면 for문 대신 forEach메서드를 사용할것을 권장한다.
Array.prototype.map
- map메서드는 자신을 호출한 배열의 모든 요소를 순회하면서 인수로 전달받은 콜백함수를 반복호출함.
그리고 콜백함수의 반환값들로 구성된 새로운 배열을 반환.
이 때 원본 배열은 변경되지 않음!const numbers = [ 1, 4, 9]; const roots = numbers.map(item => Math.sqrt(item)); // 위 코드는 아래와 같음 // const roots = numbers.map(Math.sqrt); console.log(roots); // [ 1, 2, 3 ] console.log(numbers); // [1, 4, 9 ] // map메서드는 원본배열을 변경하지 않음.
- forEach 메서드와 유사해보이지만 forEach 메서드는 undefined를 반환하고 map 메서드는 콜백함수의 반환값들로 구성된 새로운 배열을 반환한다는 차이가 있음.
즉, forEach 메서드는 단순히 반복문을 대체하기위한 고차함수이고,
map 메서드는 요소값을 다른값으로 매핑(mapping)한 새로운 배열을 생성하기위한 고차함수임! -
- map 메서드는 콜백함수를 호출할 때 3개의 인수를 순차적으로 전달한다.
- map 메서드를 호출한 배열의 요소값, 인덱스, map 메서드를 호출한 배열(this).
- forEach 메서드와 마찬가지로 map 메서드의 두번쨰 인수로 map 메서드의 콜백함수 내부에서 this로 사용할 객체를 전달할 수 있다.
Array.prototype.filter
-
filter 메서드는 자신을 호출한 배열의 모든 요소를 순회하면서 인수로 전달받은 콜백함수를 반복호출함.
그리고 콜백함수의 반환값이 true인 요소로만 구성된 새로운 배열을 반환함
(원본 배열은 변경되지 않음!)ex)
javascript const numbers = [1, 2, 3, 4, 5]; const odds = nuumbers.filter(item => item % 2); console.log(odds); // [ 1, 3, 5]
. - filter 메서드는 자신을 호출한 배열에서 필터링 조건을 만족하는 특정 요소만 추출하여 새로운 배열을 만들고 싶을 때 사용한다.
filter 메서드가 생성하여 반환한 새로운 배열의 length값은 filter 메서드를 호출한 배열의 length값과 같거나 작음. - filter 메서드는 콜백함수를 호출할 때 3개의 인수, 배열의 요소값과 인덱스, filter 메서드를 호출한 배열(this)를 순차적으로 전달한다.
[ 1, 2, 3 ].filter((item, index, arr) => { console.log(`요소값: ${item}, 인덱스: ${index}, this: ${JSON.stringify(arr)}`); return itme % 2; }); /* 요소값: 1, 인덱스: 0, this: [1, 2, 3] 요소값: 2, 인덱스: 1, this: [1, 2, 3] 요소값: 3, 인덱스: 2, this: [1, 2, 3] */
Subscribe via RSS