배열과 메서드

김하은·2023년 5월 15일
0

배열은 다양한 메서드를 제공한다.

요소 추가, 제거 메서드

배열의 맨 앞이나 끝에 요소를 추가하거나 제거하는 메서드는 앞서 언급을 했었다.

  • arr.push(...items) 는 맨 끝에 요소를 추가하는 것
  • arr.pop() 맨 끝의 요소를 제거한다.
  • arr.shift() 맨 앞의 요소를 제거한다.
  • arr.unshift(...items) 맨 앞에 요소를 추가한다.

이외

splice

배열에서 요소를 하나만 지우고 싶을 때 =>
배열 역시 객체형에 속하기에 프로퍼티를 지울 때 사용하는 연산자 delete를 사용할 수 있다.

delete obj.key는 key를 이용해 해당 키에 상응 하는 값을 지우기 때문에 요소는 지워저 undefined로 나오지만 길이는 그대로 출력된다.

delete메서드는 제역할을 다 한것이다.
그러나 우리가 원하는 방식은 하나가 지워지면 배열의 길이도 줄어드는 것이다.

이런 기대를 충족시키려면 특별한 메서드를 사용할 필요가 있다.

arr.splice(start) 는 요소를 자유자재로 다룰 수 있게 해준다.
이 메서드를 사용하면 요소를 추가, 삭제, 교체하는 것 까지 모두 가능하다.

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

첫번째 매개변수는 조작을 가할 첫번째 요소를 가리키는 인덱스이다. 두번
째 요소는 제거하고자 하는 요소의 개수이고 그 다음에 오는것들은 배열에 추가할 요소를 나타낸다.

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

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

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

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

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

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

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

인덱스 2번부터 새로운 요소가 추가가된다.

음수 인덱스도 사용할 수 있다.
splice뿐만 아니라 배열 관련 메서드에서는 음수 인덱스를 사용할 수 있다. 이때 마이너스 부호의 숫자는 배열 끝에서 부터 카운트 된 요소이다.

let arr = [1, 2, 5];

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

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

slice

arr.slice는 arr.splice와 유사하면서도 훨씬 간단하다.

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

이 메서드는 start부터 end를 제외하고 그 앞까지의 요소를 복사한 새로운 배열을 반환한다. start와 end둘다 음수 일 수 있는데 이 경우에는 배열 끝에서부터의 요소개수를 의미한다.

arr.slice는 문자열 메서드인 str.slice와 유사하게 작동한다.
다만 차이점은 arr.slice는 서브 문자열 대신 서브 배열을 반환한다는 점이다.

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

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

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

concat

arr.concat은 기존 배열의 요소를 사용해 새로운 배열을 만들거나 기존 배열에 요소를 추가하고자할 때 사용할 수 있다.

arr.concat(arg1, arg2...)

인수에는 배열이나 값이 올 수 있고 인수의 개수에는 제한이 없다.

매서드를 호출하면arr에 속한 모든 요소와 arg1, arg2 등에 속한 모든 요소를 모은 새로운 배열이 반환된다.

인수 argN이 배열일 경우 배열의 모든 요소가 복사가 되고 아니라면 인수가 그대로 복사된다.

let arr = [1, 2];

// arr의 요소 모두와 [3,4]의 요소 모두를 한데 모은 새로운 배열이 만들어집니다.
alert( arr.concat([3, 4]) ); // 1,2,3,4

// arr의 요소 모두와 [3,4]의 요소 모두, [5,6]의 요소 모두를 모은 새로운 배열이 만들어집니다.
alert( arr.concat([3, 4], [5, 6]) ); // 1,2,3,4,5,6

// arr의 요소 모두와 [3,4]의 요소 모두, 5와 6을 한데 모은 새로운 배열이 만들어집니다.
alert( arr.concat([3, 4], 5, 6) ); // 1,2,3,4,5,6

concat메서드는 제공받은 배열의 요소를 복사해 활용한다.
객체가 인자로 넘어오면 객체는 분해되지 않고 통으로 복사되어 더해진다.

let arr = [1, 2];

let arrayLike = {
  0: "something",
  length: 1
};

alert( arr.concat(arrayLike) ); // 1,2,[object Object]

그런데 인자로 받은 유사 배열 객체에 특수한 프로퍼티 Symbol.isConcatSpreadable이 있으면 concat은 이 객체를 배열처럼 취급한다.
따라서 객체 전체가 아닌 객체 프로퍼티의 값이 더해진다.

let arr = [1, 2];

let arrayLike = {
  0: "something",
  1: "else",
  [Symbol.isConcatSpreadable]: true,
  // 얘가 있기에 concat에서는 이 객체를 배열로 봄
  length: 2
}; 

alert( arr.concat(arrayLike) ); // 1,2,something,else

forEach로 반복작업 하기

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

arr.forEach((item,index,array)=>{
  // 요소에 무언가를 할 수 있음.
})
const aniName = ["Bilbo", "Gandalf", "Nazgul"]
  aniName.forEach(alert);
// 각 요소들 모두를 alert창을 통해 출력해줌.
aniName.forEach((item, index, array) => {
  alert(`${item} is at index ${index} in ${array}`);
});
// 요소와 인덱스까지 출력해줌

추가적으로 인수로 넘겨준 함수의 반환값은 무시된다.

배열 탐색하기

베열 내에서 무언가를 찾고 싶을 때 쓰는 메서드를 알아본다.

  • indexOf, lastIndexOf와 includes
    arr.indexOf와 arr.lastIndexOf, arr.includes는 같은 이름을 가진 문자열 메서드와 문법이 동일하며 하는 일도 같다.
    연산대상이 문자열이 아닌 배열의 요소라는 점만 다르다.
  • arr.indexOf(item, from)는 인덱스 from부터 시작해 item(요소)을 찾음, 요소를 발견하면 해당 요소의 인덱스를 반환하고, 발견하지 못하면 -1을 반환한다.
  • arr.lastIndexOf(item, from)은 위 메서드와 동일한 기능을하고, 검색을 끝에서 부터 시작한다는 점만 다르다.
  • arr.includes(item,from)은 인덱스 from부터 시작해 item이 있는지 검색하고 있다면 true를 반환한다.(없으면 false)

다만 위의 메서드들은 요소를 찾을때 완전 항등 연산자 ===를 사용한다.
false를 검색하면 정확히 false만을 검색한다.

요소의 위치보다 요소 존재 여부만 알고 싶다면 arr.includes를 사용하자.

includes는 NaN도 제대로 처리한다는 점에서 indexOf/lastIndexOf와 약간의 차이가 있다.

  • find와 findIndex
    객체로 이루어진 배열이 있다고 가정했을때 특정 조건에 부합하는 객체를 배열 내에서 어떻게 찾을까?

이럴때 사용하는것이 arr.find(fn)이다

let result = arr.find(function(item, index, array) {
  // true가 반환되면 반복이 멈추고 해당 요소를 반환합니다.
  // 조건에 해당하는 요소가 없으면 undefined를 반환합니다.
});

요소 전체를 대상으로 함수가 순차적으로 호출된다.

  • item은 함수를 호출한 요소를,
  • index는 요소의 인덱스를
  • array는 배열 자기자신을 의미한다.

함수가 참을 반환하면 탐색이 중단되고 해당 요소가 반환된다.
원하는 요소를 찾지 못했다면 undefined가 반환된다.

id 와 name프로퍼티를 가진 사용자 객체로 구성된 배열을 예로들자면
배열 내에서 id == 1 조건을 충족하는 사용자 객체를 찾아보자

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

let user = users.find(item => item.id == 1);

alert(user.name); // John

실무에서는 객체로 구성된 배열을 다루어야 할 일이 많기에 find메서드를 알아두는 것도 좋다.

그런데 위에서는 finde안의 함수가 인자를 하나(item만) 만 가지고 있다.
(item => item.id ==1 )은 많이 활용되는 패턴이다. 다른인자들은 많이 사용되지 않는다.

arr.findIndex는 find과 동일한 일을 하지만 조건에 맞는 요소를 반환하는 대신 조건에 맞는 요소의 인덱스를 반환한다.
조건에 맞는 요소가 없다면 -1이 반환된다.

filter

find메서드는 함수의 반환값을 true로 만드는 단 하나의 요소를 찾는다.
조건을 충족하는 요소가 여러개라면 arr.filter(fn)을 사용하면된다.
filter는 find와 문법이 유사하긴 하지만 , 조건에 맞는 요소 전체를 담은 배열을 반환한다는 점에서 차이가 있다.

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); // [ {id: 1, name: "John"}, {id: 2, name: "Pete"},];

alert(someUsers.length); // 2


#### 배열을 변형하는 메서드
배열을 변형시키거나 요소를 재정렬해주는 메서드에 대해 알아보자

- map
arr.map은 유용성과 사용빈도가 아주 높다.

map은 배열 요소 전체를 대상으로 함수를 호출하고, 함수 호출 결과를 배열로 반환해준다.

=> 새로운 값을 반환

```js
let result = arr.map(function(item, index, array) {
  // 요소 대신 새로운 값을 반환합니다.
});
let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length);
alert(lengths); // 5,7,6
  • sort(fn)
    arr.sort()는 배열의 요소를 정리해준다. 배열 자체가 변경된다.
    => 재정렬된 배열이 반환되는데 arr자체가 수정되었기에 반환값은 잘 사용되지 않는다.

요소는 문자열로 취급되어 재정렬이되고 문자열 비교는 사전 편집순으로 진행되어 예상치 못한 결과를 받아볼 수 있다.

기본 정렬 기준 대신 새로운 정렬 기준을 만들려면 arr.sort()에 새로우 함수를 넘겨주어야한다.
인수로 넘겨주는 함수는 반드시 값 두개를 비교해야하고, 반환값도 필요하다.

function compare(a, b) {
  if (a > b) return 1; // 첫 번째 값이 두 번째 값보다 큰 경우
  if (a == b) return 0; // 두 값이 같은 경우
  if (a < b) return -1; //  첫 번째 값이 두 번째 값보다 작은 경우
}

정렬함수는 어떤 숫자든 반환할 수 있다.
정렬함수의 반환 값에는 제약이 없다.
양수를 반환하는 경우, 첫번째 인수가 두번째 인수보다 '크다'를 나타내고, 음수를 반환하는 경우 첫번째 인수가 두번째 인수보다 '작다'를 나타낸다.
이것을 이용해 더 간단히 표현할 수 있다.

let arr = [ 1, 2, 15 ];

arr.sort(function(a, b) { return a - b; });

alert(arr);  // 1, 2, 15
arr.sort((a,b) => a > b ? 1 : -1)
// [1, 2, 15] // 오름차순
arr.sort((a,b) => a > b ? -1 : 1)
// [15, 2, 1] // 내림차순

문자열에는 localCompate를 사용하자
strings에서 나온 알고리즘. 유니코드를 기준으로 글자를 비교하는 알고리즘이다.
Ö같은 문자가 있는 언어에도 대응하려면 str.localeCompare 메서드를 사용해 문자열을 비교하는 게 좋다.

let countries = ['Österreich', 'Andorra', 'Vietnam'];

alert( countries.sort( (a, b) => a > b ? 1 : -1) ); // Andorra, Vietnam, Österreich (제대로 정렬이 되지 않았습니다.)

alert( countries.sort( (a, b) => a.localeCompare(b) ) ); // Andorra,Österreich,Vietnam (제대로 정렬되었네요!)
  • reverse

arr.reverse는 arr의 요소를 역순으로 정렬시켜주는 메서드이다.
반환값은 재정렬된 배열.

  • split과 join
    split메서드는 구분자를 기준으로 문자열을 쪼개준다.
let names = 'Bilbo, Gandalf, Nazgul';

let arr = names.split(', ');

for (let name of arr) {
  alert( `${name}에게 보내는 메시지` ); // Bilbo에게 보내는 메시지
// Gandalf에게 보내는 메시지
  // Nazgul에게 보내는 메시지
}

split메서드는 두번째 인수로 숫자를 받을 수 있다.
이 숫자는 배열의 길이를 제한해주기에 길이를 넘어서는 요소를 무시할 수 있다.

let arr = 'Bilbo, Gandalf, Nazgul, Saruman'.split(', ', 2);

alert(arr); // Bilbo, Gandalf

arr.join은 split과 반대 역할을 한다. 구분자를 사용해 배열의 요소를 하나의 문자열로 만들어준다.

  • reduce와 reduceRight

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

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

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

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

  • accumulator – 이전 함수 호출의 결과. initial은 함수 최초 호출 시 사용되는 초깃값을 나타냄(옵션값)
  • item – 현재 배열 요소
  • index – 요소의 위치
  • array – 배열
    이전 함수 호출 결과는 다음 함수를 호출할 때 첫 번째 인수(previousValue)로 사용된다

첫 번째 인수는 앞서 호출했던 함수들의 결과가 누적되어 저장되는 '누산기'이다
마지막 함수까지 호출되면 이 값은 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 이고 여기에 배열의 다음 요소가 더해집니다. 이런 과정이 계속 이어집니다.

초기값을 생략할 수도 있는데 , 초기값이 없다면 배열의 첫번째 요소를 초기값으로 사용하고 두번째 요소부터는 함수를 호출하기 때문이다.

다만 초기값 없이 reduce를 사용할시 빈 배열에 적용하였다면 배열이 빈배열이기에 reduce호출시 에러를 발생시킨다.(초기값은 없으면 배열의 첫번째요소가 초기값이 되는데 빈 배열이므로..)

arr.reduceRight는 reduce와 동일한 기능을 하나 배열의 오른쪽부터 연산을 수행한다.

Array.isArray를 사용해 배열 여부 알아내기

자바스크립트에서 배열은 독립된 자료형으로 취급되지 않고 객체에 속한다.
따라서 typeof를 사용해도 일반 객체와 배열을 구분할 수는 없다.

=>object를 반환

그러나 배열은 자주 사용되는 자료구조이기에 배열인지 아닌지를 감별해내는 특별한 메서드가 있으면 유용할 것이다.
이 메서드가 바로 Array.isArray(value)이다.
value가 배열이라면 true를 , 아니라면 false를 반환한다.

alert(Array.isArray({})); // false

alert(Array.isArray([])); // true

배열 메서드와 thisArg

함수를 호출하는 대부분의 배열 메서드(find, filter, map 등. sort는 제외)는 thisArg라는 매개변수를 옵션으로 받을 수 있다.

자주사용하는 인수가 아니라 아직 언급되지는 않았지만 튜토리얼의 완성도를 위해 thisArg에 대해 알아보자

thisArg는 아래와 같이 활용할 수 있다.

arr.find(func, thisArg);
arr.filter(func, thisArg);
arr.map(func, thisArg);
// ...
// thisArg는 선택적으로 사용할 수 있는 마지막 인수입니다.

thisArg는 func의 this가 된다.
...

출처: https://ko.javascript.info/array-methods

0개의 댓글