배열과 메서드

Christy·2020년 9월 14일
0

Today I Learn

목록 보기
2/3
post-thumbnail

배열은 다양한 메서드를 제공한다. 그 중 splice, slice, forEach, filter, map, reduce, 그리고 reverse에 대해 정리해본다.

splice

arr.splice(start)는 만능 스위스 맥가이버 칼 같은 메서드이다. 요소를 자유자재로 다룰 수 있게 해준다. 이 메서드를 사용하면 요소 추가, 삭제, 교체가 모두 가능하다.

문법은 다음과 같다.

arr.splice(index[, deleteCount, elem1, ...., elemN])

첫번째 매개변수는 조작을 가할 첫 번째 요소를 가리키는 인덱스(index)이다. 두번째 매개변수는 deleteCount로 제거하고자 하는 요소의 개수를 나타낸다. elem1, ..., elemN은 배열에 추가할 요소를 나타낸다.

splice메서드를 사용해 작성된 예시 몇 가지를 보자.
먼저 요소 삭제에 관한 예시부터 살펴보면,

let arr = ['I', 'study', 'JavaScript'];

arr.splice(1, 1); // 인덱스 1부터 요소 한 개를 제거

alert( arr ); // ['I', 'JavaScript']

인덱스 1이 가리키는 요소부터 시작해 요소 한개를(1)를 지웠다.
다음 예시에선 요소 세개(3)를 지우고, 그 자리를 다른 요소 두개로 교체해 보도록 하자.

let arr = ['I', 'study', 'JavaScript', 'right', 'now']

// 처음(0) 세개(3)의 요소를 지우고, 이 자리를 다른 요소로 대체한다.
arr.splice(0, 3, 'Let's', 'dance']

alert( arr ) // now ['Let's', 'dance', 'right', 'now'];

splice는 삭제된 요소로 구성된 배열을 반환한다. 아래 예시를 통해 확인해 보자.

let arr = ['I', 'study', 'JavaScript', 'right', 'now'];

//처음 두개의 요소를 삭제함.
let removed = arr.splice(0, 2);

alert( removed ); // 'I', 'study' <--삭제된 요소로 구성된 배열

splice메서드의 deleteCount를 0으로 설정하면 요소를 제거하지 않으면서 새로운 요소를 추가할 수 있다.

let arr = ['I', 'study', 'JavaScript'];

// 인덱스 2부터
// 0개의 요소를 삭제한다.
// 그 후, 'complex'와 'language'를 추가한다.
arr.splice(2, 0, 'complex', 'language');

alert( arr ); // 'I', 'study', 'complex', 'language', 'JavaScript'

음수 인덱스도 사용할 수 있다.
slice메서드 뿐만 아니라 배열 관련 메서드엔 음수 인덱스를 사용할 수 있다. 이때 마이너스 부호 앞의 숫자는 배열 끝에서부터 센 요소 위치를 나타낸다.

let arr = [1, 2, 5];

// 인덱스 -1부터 (배열 끝에서부터 첫 번째 요소)
// 0개의 요소를 삭제하고
// 3과 4를 추가한다.
arr.splice(-1, 0, 3, 4);

alert( arr ); // 1, 2, 3, 4, 5

slice

arr.slicearr.splice와 유사해 보이지만 훨씬 간단하다.

문법:

arr.slice([start], [end])

이 메서드는 'start'인덱스부터('end'를 제외한) 'end'인덱스까지의 요소를 복사한 새로운 배열을 반환한다. start와 end는 둘 다 음수일 수 있는데 이땐, 배열 끝에서부터의 요소 개수를 의미한다.

arr.slice는 문자열 메서드인 str.slice와 유사하게 동작하는데 arr.slice는 서브 문자열 (substring) 대신 서브 배열(subarray)을 반환한다는 점이 다르다.

예시:

let arr = ['t', 'e', 's', 't'];

alert( arr.slice(1, 3) ); // e,s (인덱스가 1인 요소부터 인덱스가 3인 요소까지를 복사(인덱스가 3인 요소는 제외))

alert( arr.slice(-2) ); // s,t (인덱스가 -2인 요소부터 제일 끝 요소까지를 복사)

arr.slicearr.slice()처럼 인수를 하나도 넘기지 않으면서 호출할 수 있다. 이렇게 하면 arr이 복사가 된다. 이런 방식은 기존의 배열을 건드리지 않으면서 배열을 조직해 새로운 배열을 만들고자 할 때 자주 사용된다.

forEach로 반복작업 하기

arr.forEach는 주어진 함수를 배열 요소 각각에 대해 실행할 수 있게 해준다.

문법:

arr.forEach(function(item, index, array) {
  // 요소에 무언가를 할 수 있다.
});

아래는 요소 모두를 alert창을 통해 출력해주는 코드이다.

// for each element call alert
['Bilbo', 'Gandalf', 'Nazgul'].forEach(alert);

아래는 인덱스 정보까지 더해서 출력해주는 좀 더 정교한 코드이다.

['Bilbo', 'Gandalf', 'Nazgul'].forEach((item, index, arry) => {
  alert(`${item} is at index ${index} in ${array}`);
});

참고로, 인수로 넘겨준 함수의 반환값은 무시된다.

filter

filterfind와 문법이 유사하지만, 조건에 맞는 요소 전체를 담은 배열을 반환한다는 점에서 차이가 있다.

let results = arr.filter(function(item, index, array) {
  // 조건을 충족하는 요소는 results에 순차적으로 더해진다.
  // 조건을 충족하는 요소가 하나도 없으면 빈 배열이 반환된다.
});

예시:

let users = [
  {id: 1, name: 'John'},
  {id: 2, name: 'Pete'},
  {id: 3, name: 'Mary'}
];

// 앞쪽 사용자 두명을 반환한다.
let someUsers = users.filter(item => item.id < 3);

alert(someUsers.length); // 2

map

arr.map은 유용성과 사용 빈도가 아주 높은 메서드 중 하나이다.
map은 배열 요소 전체를 대상으로 함수를 호출하고, 함수 호출 결과를 반환해준다.

문법:

let result = arr.map(function(item, index, array) {
  // 요소 대신 새로운 값을 반환한다.
});

아래 예시에선 각 요소(문자열)의 길이를 출력해준다.

let lengths = ['Bilbo', 'Gandalf', 'Nazgul'].map(item => item.length);
alert(lengths); // 5,7,6

reduce와 reduceRight

forEach, for, for..of를 사용하면 배열 내 요소를 대상으로 반복 작업을 할 수 있다.
각 요소를 돌면서 반복 작업을 수행하고, 작업 결과물을 새로운 배열 형태로 얻으려면 map을 사용하면 된다.

arr.reducearr.reduceRight도 이런 메서드들과 유사한 작업을 해준다. 그런데 사용법이 조금 복잡하다. reducereduceRight는 배열을 기반으로 값 하나를 도출할 때 사용된다.

문법:

let value = arr.reduce(function(accumulator, item, index, array) {
  // ...
}, [initial]);

인수로 넘겨주는 함수는 배열의 모든 요소를 대상으로 차례차례 적용되는데, 적용 결과는 다음 함수 호출시 사용된다.

함수의 인수는 다음과 같다.

  • accumulator - 이전 함수 호출의 결과. initial은 함수 최초 호출 시 사용되는 초기값을 나타낸다.
    (옵션)
  • item - 현재 배열 요소
  • index - 요소의 위치
  • array - 배열

이전 함수 호출 결과는 다음 함수를 호출할 때 첫번째 인수(previousValue)로 사용된다.

첫 번째 인수는 앞서 호출했던 함수들의 결과가 누적되어 저장되는 '누산기(accumulator)'라고 생각하면 된다. 마지막 함수까지 호출되면 이 값은 reduce의 반환값이 된다.

복잡해 보이긴 하지만 예제를 통해 메서드를 이해해 보자.

reduce를 이용해 코드 한 줄로 배열의 모든 요소를 더한 값을 구해보자.

let arr = [1, 2, 3, 4, 5];

let result = arr.reduce((sum, current) => sum + current, 0);

alert(result); // 15

reduce에 전달한 함수는 오직 인수 두개만 받고 있다. 대개 이렇게 인수를 두개만 받는다.

이제 어떤 과정을 거쳐 위와 같은 결과가 나왔는지 자세히 살펴보자.

  1. 함수 최초 호출시, reduce의 마지막 인수인 0(초기 값)이 sum에 할당된다. current엔 배열의 첫번째 요소인 1이 할당된다. 따라서 함수의 결과는 1이 된다.

  2. 두번째 호출시, sum = 1이고 여기에 배열의 두번째 요소( 2 )가 더해지므로 결과는 3이 된다.

  3. 세번째 호출시, sum = 3이고 여기에 배열의 다음 요소가 더해진다. 이런 과정이 계속 이어진다.

    한편, 아래와 같이 초기값을 생략하는 것도 가능하다.

let arr = [1, 2, 3, 4, 5];

// reduce에서 초기값을 제거함(0이 없음)
let result = arr.reduce(sum, current) => sum + current);

alert( result ); // 15

초기값을 없애도 결과는 동일하다. 초기값이 없으면 reduce는 배열의 첫번째 요소를 초기값으로 사용하고 두번째 요소부터 함수를 호출하기 때문이다.

하지만 이렇게 초기값 없이 reduce를 사용할 땐 극도의 주의를 기울어야 한다. 배열이 비어있는 상태면 reduce호출 시 에러가 발생하기 때문이다.

예시:

let arr = [];

// TypeError: Reduce of empty array with no inital value
// 초기값을 설정해 주었다면 초기값이 반환되었을 것이다.
arr.reduce((sum, current) => sum + current);

이런 예외상황 때문에 항상 초기값을 명시해 줄 것을 권장한다.
arr.reduceRightreduce와 동일한 기능을 하지만 배열의 오른쪽부터 연산을 수행한다는 점이 다른 메서이다.

reverse

arr.reversearr의 요소를 역순으로 정렬시켜주는 메서드이다.

예시:

let arr = [1, 2, 3, 4, 5];
arr.reverse();

alert( arr ); // 5,4,3,2,1

반환값은 재정렬된 배열이다.

요약

지금까지 살펴본 배열 메서드를 요약해 본다.

  • 요소를 더하거나 지우기
    • splice(pos, deleteCount, ...items) -pos부터 deleteCount개의 요소를 지우고 items 추가하기
    • slice(start, end) - start부터 end 바로 앞까지 의 요소를 복사해 새로운 배열을 만듦
  • 원하는 요소찾기
    • filter(func) - func의 반환값을 true로 만드는 첫번째/전체 요소를 반환함.
  • 배열 전체 순회하기
    • forEach(func) - 모든 요소에 func을 호출함.
      결과는 반환되지 않음
  • 배열 변형하기
    • map(func) - 모든 요소에 func을 호출하고, 반환된 결과를 가지고 새로운 배열을 만듦
    • reverse() - 배열을 뒤집어 반환함.
    • reduce(func, initial) - 요소를 차례로 돌면서 func을 호출함. 반환값은 다음 함수 호출에 전달함.
      최종적으로 하나의 값이 도출됨.

0개의 댓글