[모던 JavaScript 튜토리얼] 24. 배열 메서드
splice
let arr = [1, 2, 3];
delete arr[0];
console.log(arr); // [ <1 empty item>, 2, 3 ]
console.log(arr.length); // 3
위 코드는, 배열 요소 하나를 객체 프로퍼티 제거 연산자 delete
를 통해 0번 째 요소를 지우는 동작을 한다. delete
는 키에 해당하는 값을 지우기 때문에, 값이 할당되지 않은 상태를 의미하는 undefined 로 설정된다. 즉, 제대로 지워졌다고 볼 수 있다. 하지만, 우리는 값을 지운 만큼 공간도 지워지길 바라며, 이에 따른 length
프로퍼티의 변화도 원한다.
arr.splice(index [, deleteCount, elem1, …, elemN])
let arr = ["I", "study", "JavaScript"];
arr.splice(1, 1);
console.log(arr); // [ "I", "JavaScript" ]
arr.plice
는 파라미터로 주어진 index
부터 deleteCount
개의 요소를 제거한다. undefined 로 남아 있는 것이 아니라, 공간 자체가 비워지고, 동시에 length 프로퍼티도 조정된다. 즉, 요소들이 빈 공간을 채워 앞으로 당겨진다.
let arr = ["I", "study", "JavaScript", "right", "now"];
arr.splice(0, 3, "Let's", "dance");
console.log(arr); // [ "Let's", "dance", "right", "now" ]
deleteCount
개의 요소를 지우고, 그 자리에 다른 요소들 elem1, ..., elemN
을 새로 넣어 줄 수도 있다.
let arr = ["I", "study", "JavaScript"];
arr.splice(2, 0, "complex", "language");
console.log(arr); // [ "I", "study", "complex", "language", "JavaScript" ]
deleteCount
를 0으로 설정해 요소를 제거하지 않으면서 요소들을 사이에 추가할 수 있다.
slice
arr.slice([start], [end])
let arr = ["t", "e", "s", "t"];
console.log(arr.slice(1, 3)); // [ "e", "s" ]
console.log(arr.slice(-2)); // [ "s", "t" ]
arr.slice
는 start 부터 end 까지의 요소들로 새로운 배열을 만들어 리턴한다.
let arr1 = ["t", "e", "s", "t"];
let arr2 = arr1.slice();
// let arr2 = Object.assign([], arr1);
arr2[0] = "d";
console.log(arr1); // [ "t", "e", "s", "t" ]
console.log(arr2); // [ "d", "e", "s", "t"]
위와 같이 인자를 전달하지 않은 채 호출해 복사본을 만들 수 있다. 배열도 객체이므로, Object.assign 을 통해서도 복사할 수 있다.
concat
arr.concat(arg1, arg2…)
let arr = [1, 2];
console.log(arr.concat([3, 4])); // 1,2,3,4
console.log(arr.concat([3, 4], [5, 6])); // 1,2,3,4,5,6
console.log(arr.concat([3, 4], 5, 6)); // 1,2,3,4,5,6
arr.concat
은 기존 배열의 요소를 사용해 새로운 배열을 만들거나, 기존 배열에 요소를 추가할 때 사용한다. 위 예에서 볼 수 있듯, 각 인자로 요소가 아닌 배열이 주어질 수도 있다.
let arr = [1, 2];
let arrayLike = {
0: "something",
length: 1,
};
console.log(arr.concat(arrayLike)); // [ 1, 2, { '0': 'something', length: 1 } ]
객체가 인자로 넘어오면, 객체는 분해되지 않고 통으로 복사되어 한 요소를 차지하게 된다.
let arr = [1, 2];
let arrayLike = {
0: "something",
1: "else",
[Symbol.isConcatSpreadable]: true,
length: 2,
};
console.log(arr.concat(arrayLike)); // [ 1, 2, 'something', 'else' ]
Symbol.isConcatSpreadable
프로퍼티가 true 로 설정되면, 객체를 배열처럼 취급해, 객체 프로퍼티의 값이 더해진다. 이 때, 키 값은 숫자여야 한다.
forEach
arr.forEach(callback(currentvalue[, index[, array]])[, thisArg])
let arr = ["Bilbo", "Gandalf", "Nazgul"];
// Bilbo is at index 0 in Bilbo,Gandalf,Nazgul
// Gandalf is at index 1 in Bilbo,Gandalf,Nazgul
// Nazgul is at index 2 in Bilbo,Gandalf,Nazgul
arr.forEach((item, index, array) => {
console.log(`${item} is at index ${index} in ${array}`);
});
arr.forEach
는 주어진 함수를 배열 요소 각각에 대해 실행할 수 있게 해준다.
let arr = ["Bilbo", "Gandalf", "Nazgul"];
console.log(arr.forEach((value, index, array) => index)); // undefined
반환 값이 undefined 임을 유의하자.
배열 탐색하기
arr.indexOf와 arr.lastIndexOf, arr.includes 의 쓰임새는 [문자열 편]과 동일하다.
arr.indexOf(item, from)
은 from 부터 시작해 인덱스를 증가시키며 item 을 찾는다.arr.lastIndexOf(item, from)
은 from 부터 인덱스를 감소시키며 item 을 찾는다.arr.includes(item, from)
은 from 부터 시작해 item 이 있는 지 찾는다.
let arr = [1, 0, false];
console.log(arr.indexOf(0)); // 1
console.log(arr.indexOf(false)); // 2
console.log(arr.indexOf(null)); // -1
console.log(arr.includes(1)); // true
비교를 할 때 동등 연산자(===)를 사용함을 유의하자.
const arr = [NaN];
console.log(arr.indexOf(NaN)); // -1
console.log(arr.includes(NaN)); // true
includes
는 NaN 도 제대로 처리한다는 점에서 indexOf
, lastIndexOf
와 차이점이 있다.
find와 findIndex
객체로 이루어진 배열의 경우, arr.find(fn)
을 통해 조건에 맞는 객체를 찾을 수 있다.
arr.find((value, index, array) ⇒ { })
let users = [
{ id: 1, name: "John" },
{ id: 2, name: "Pete" },
{ id: 3, name: "Mary" },
];
let user = users.find((item) => item.id == 1);
console.log(user.name); // John
조건에 부합하는 요소를 찾으면, 해당 객체를 리턴하고, 찾지 못하면 undefined 를 리턴한다.
filter
let users = [
{ id: 1, name: "John" },
{ id: 2, name: "Pete" },
{ id: 3, name: "Mary" },
];
let someUsers = users.filter((item) => item.id < 3);
console.log(someUsers); // [ { id: 1, name: 'John' }, { id: 2, name: 'Pete' } ]
위에서 본 find
는 조건을 만족하는 객체 하나를 리턴하는 반면, filter
는 만족하는 여러 개의 객체를 새로운 배열에 담아 리턴한다.
배열을 변형하는 메서드
map
let lengths = ["Bilbo", "Gandalf", "Nazgul"].map((value) => value.length);
console.log(lengths); // [ 5, 7, 6 ]
arr.map
은 배열의 전체 요소를 대상으로 함수를 호출한 뒤, 함수 호출 결과로 새로운 배열을 만들어 리턴한다.
sort
arr.sort() 는 배열의 요소를 정렬해준다. 메서드를 호출한 배열 자체가 변경된다.
let arr = [1, 2, 15];
arr.sort();
console.log(arr); // [ 1, 15, 2 ]
모든 요소는 문자형으로 변환된 후 정렬된다.
let arr = [1, 2, 15];
arr.sort((a, b) => a - b);
console.log(arr); // [ 1, 2, 15 ]
위와 같이, 새로운 정렬 기준을 함수로 작성해 인수로 넘길 수 있다.
reverse
let arr = [1, 2, 3, 4, 5];
arr.reverse();
console.log(arr); // [ 5, 4, 3, 2, 1 ]
arr.reverse
는 arr의 요소를 역순으로 바꾼다.
split과 join
let nums = "1 2 3 4 5";
let arr = nums.split(" ").map(Number);
console.log(arr);
str.split(delim)
은, 구분자 delim 을 기준으로 문자열을 쪼개 각각을 요소로 하는 배열을 리턴한다.
let arr = ["Bilbo", "Gandalf", "Nazgul"];
let str = arr.join();
console.log(str); // Bilbo,Gandalf,Nazgul
str.join(glue)
은 split 과 반대로, 배열 모든 요소 사이에 glue 를 넣어 문자열로 만들어 리턴한다. 파라미터로 glue 를 전달하지 않으면, 쉼표가 디폴드로 적용된다.
reduce와 reduceRight
forEach
, for
, for...of
를 사용하면 배열 내 요소들을 대상으로 반복 작업을 할 수 있다.
arr.reduce
와 arr.reduceRight
도 마찬가지로 반복 작업을 위해 사용되지만, 특정한 하나의 값을 리턴한다는 차이점이 있다. reduceRight 은 reduce 와 하는 일이 같고, 뒤에서부터 순히한다는 차이점이 있다.
arr.reduce((prev, value, index, array) ⇒ { }, initValue)
initValue를 설정하지 않으면, 초기 accumulator 는 첫 번째 인덱스, 즉 array[0]으로 설정된다. 인덱스가 홀수면 더하고, 짝수면 빼는 연산을 적용해 최종 값을 출력하는 예제를 보자.
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((prev, val, idx) => prev + (idx % 2 == 0 ? -val : val));
console.log("result : " + result); // -1
initValue
를 설정하지 않았기에, 첫 번째 요소의 인덱스가 짝수임에도 불구하고 prev
가 1로 사용되고, 다음 번 요소부터 리듀서 함수를 적용시킨다. 따라서, 잘못된 정답 -1이 나오게 된다.
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((prev, val, idx) => prev + (idx % 2 == 0 ? -val : val), 0);
console.log("result : " + result); // -1
initValue
를 0으로 설정함으로써, 첫 번째 인덱스부터 순회를 시작하게 된다.
배열 여부 알아내기
let obj = { name: "Kim", age: 28 };
let arr = [1, 2, 3];
console.log(typeof obj); // object
console.log(typeof arr); // object
배열도 객체기에, typeof 로는 object 가 출력된다.
let obj = { name: "Kim", age: 28 };
let arr = [1, 2, 3];
console.log(Array.isArray(obj)); // false
console.log(Array.isArray(arr)); // true
배열인지 확인을 위해서는 Array.isArray()
를 사용하자.
thisArg
배열 메서드 대부분의 마지막 파라미터로 thisArg
가 전달된다. 이는 파라미터로 전달된 콜백 함수 내부에 this 가 사용시, this 의 범위를 설정하기 위해 사용된다.
let army = {
minAge: 18,
maxAge: 27,
canJoin(user) {
return user.age >= this.minAge && user.age < this.maxAge;
},
};
let users = [{ age: 16 }, { age: 20 }, { age: 23 }, { age: 30 }];
let soldiers = users.filter(army.canJoin, army);
console.log(soldiers.length); // 2
console.log(soldiers[0].age); // 20
console.log(soldiers[1].age); // 23
객체 army 의 내부 메서드 canJoin 을 콜백 함수로 설정해 filter 메서드를 실행했다. 여기서 army 라는 객체를 전달하지 않으면, canJoin 내부에서의 this 는 global object 로 잘못 인식되어 에러가 발생하게 된다.
댓글남기기