JavaScript
[모던 자바스크립트] 배열(2) - 코딩테스트 푸는 시간 단축 해드림!!
jjin502
2022. 8. 19. 13:24
자바스크립트에서 배열은 굉장히 많이 사용되는 자료구조이다. 자바스크립트 배열은 다양한 메서드를 제공하고 이는 굉장히 유용하게 쓰인다. (특히 코딩테스트 풀때...)
배열 메서드가 결과물을 반환하는 패턴
- 원본 배열을 직접 변경하는 메서드
- 원본 배열 : 배열 메서드를 호출한 배열, 즉 배열 메서드의 구현체 내부에서 this가 가리키는 객체
- 원본 배열을 직접 변경하지 않고 새로운 배열을 생성해 반환하는 메서드
const arr = [1];
// push 메서드는 원본 배열(arr)을 직접 변경한다.
arr.push(2);
console.log(arr); // [1, 2]
// concat 메서드는 원본 배열(arr)을 직접 변경하지 않고 새로운 배열을 생성하여 반환한다.
const result = arr.concat(3);
console.log(arr); // [1, 2]
console.log(result); // [1, 2, 3]
주의
원본 배열을 직접 변경하는 메서드는 외부 상태를 직접 변경하는 부수효과가 있으므로 사용에 주의해야한다.
Array.isArray
전달된 인수가 배열이면 true, 아니면 false를 반환한다.
// true
Array.isArray([]);
Array.isArray([1, 2]);
Array.isArray(new Array());
// 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
원본 배열에서 인수로 전달된 요소를 검색해 인덱스를 반환한다.
const arr = [1, 2, 2, 3];
// 배열 arr에서 요소 2를 검색하여 첫 번째로 검색된 요소의 인덱스를 반환한다.
arr.indexOf(2); // -> 1
// 배열 arr에 요소 4가 없으므로 -1을 반환한다.
arr.indexOf(4); // -> -1
// 두 번째 인수는 검색을 시작할 인덱스다. 두 번째 인수를 생략하면 처음부터 검색한다.
arr.indexOf(2, 2); // -> 2
배열에 특정 요소가 존재하는지 그 존재 여부를 확인할 때 유용하다
const foods = ['apple', 'banana', 'orange'];
// foods 배열에 'orange' 요소가 존재하는지 확인한다.
if (foods.indexOf('orange') === -1) {
// foods 배열에 'orange' 요소가 존재하지 않으면 'orange' 요소를 추가한다.
foods.push('orange');
}
console.log(foods); // ["apple", "banana", "orange"]
참고 : indexOf 메서드 대신 ES7에서 도입된 Array.prototype.includes 메서드를 사용하면 가독성이 좋아진다
const foods = ['apple', 'banana'];
// foods 배열에 'orange' 요소가 존재하는지 확인한다.
if (!foods.includes('orange')) {
// foods 배열에 'orange' 요소가 존재하지 않으면 'orange' 요소를 추가한다.
foods.push('orange');
}
console.log(foods); // ["apple", "banana", "orange"]
Array.prototype.push
인수로 전달 받은 모든 값을 원본 배열의 마지막 요소로 추가하고 변경된 length프로퍼티 값을 반환한다. push메서드는 원본 배열을 직접 변경한다는 것을 기억하자.
const arr = [1, 2];
// 인수로 전달받은 모든 값을 원본 배열 arr의 마지막 요소로 추가하고 변경된 length 값을 반환한다.
let result = arr.push(3, 4);
console.log(result); // 4
// push 메서드는 원본 배열을 직접 변경한다.
console.log(arr); // [1, 2, 3, 4]
push는 성능면에서 좋지 않다.
- 마지막 요소로 추가할 요소가 하나 뿐이라면 length 프로퍼티를 활용하여 마지막 요소를 직접 추가 할 수 있다. 이 방법이 push보다 빠르다
const arr = [1, 2];
// arr.push(3)과 동일한 처리를 한다. 이 방법이 push 메서드보다 빠르다.
arr[arr.length] = 3;
console.log(arr); // [1, 2, 3]
참고 : push는 원본 배열을 직접 변경하는 부수효과가 있기 때문에 스프레드(...)문법을 권장한다
- 스프레드 문법을 사용하면 함수 호출 없이 표현식으로 마지막에 요소를 추가할 수 있다.
const arr = [1, 2];
// ES6 스프레드 문법
const newArr = [...arr, 3];
console.log(newArr); // [1, 2, 3]
Array.prototype.pop
원본 배열에서 마지막 요소를 제거하고 제거한 요소를 반환한다. 이때, 빈 배열일 경우 undefined를 반환한다.
const arr = [1, 2];
// 원본 배열에서 마지막 요소를 제거하고 제거한 요소를 반환한다.
let result = arr.pop();
console.log(result); // 2
// pop 메서드는 원본 배열을 직접 변경한다.
console.log(arr); // [1]
장점 : stack & queue에 유용하게 쓸 수 있다.
스택을 생성자 함수를 이용해서 구현하면 아래와 같다.
const Stack = (function () {
function Stack(array = []) {
if (!Array.isArray(array)) {
// "47. 에러 처리" 참고
throw new TypeError(`${array} is not an array.`);
}
this.array = array;
}
Stack.prototype = {
// "19.10.1. 생성자 함수에 의한 프로토타입의 교체" 참고
constructor: Stack,
// 스택의 가장 마지막에 데이터를 밀어 넣는다.
push(value) {
return this.array.push(value);
},
// 스택의 가장 마지막 데이터, 즉 가장 나중에 밀어 넣은 최신 데이터를 꺼낸다.
pop() {
return this.array.pop();
},
// 스택의 복사본 배열을 반환한다.
entries() {
return [...this.array];
}
};
return Stack;
}());
const stack = new Stack([1, 2]);
console.log(stack.entries()); // [1, 2]
stack.push(3);
console.log(stack.entries()); // [1, 2, 3]
stack.pop();
console.log(stack.entries()); // [1, 2]
Array.prototype.unshift
인수로 전달받은 모든 값을 원본 배열의 선두에 요소로 추가하고 변경된 length프로퍼티 값을 반환한다.
const arr = [1, 2];
// 인수로 전달받은 모든 값을 원본 배열의 선두에 요소로 추가하고 변경된 length 값을 반환한다.
let result = arr.unshift(3, 4);
console.log(result); // 4
// unshift 메서드는 원본 배열을 직접 변경한다.
console.log(arr); // [3, 4, 1, 2]
- 원본 배열을 직접 변경하는 부수효과가 있기 때문에 ES6에서는 스프레드(...)문법 사용을 추천한다.
Array.prototype.shift
원본 배열에서 첫 번째 요소를 제거하고 제거한 요소를 반환한다.
const arr = [1, 2];
// 원본 배열에서 첫 번째 요소를 제거하고 제거한 요소를 반환한다.
let result = arr.shift();
console.log(result); // 1
// shift 메서드는 원본 배열을 직접 변경한다.
console.log(arr); // [2]
Shift + push 메서드를 사용하면 Queue를 쉽게 구현할 수 있다.
const Queue = (function () {
function Queue(array = []) {
if (!Array.isArray(array)) {
// "47. 에러 처리" 참고
throw new TypeError(`${array} is not an array.`);
}
this.array = array;
}
Queue.prototype = {
// "19.10.1. 생성자 함수에 의한 프로토타입의 교체" 참고
constructor: Queue,
// 큐의 가장 마지막에 데이터를 밀어 넣는다.
enqueue(value) {
return this.array.push(value);
},
// 큐의 가장 처음 데이터, 즉 가장 먼저 밀어 넣은 데이터를 꺼낸다.
dequeue() {
return this.array.shift();
},
// 큐의 복사본 배열을 반환한다.
entries() {
return [...this.array];
}
};
return Queue;
}());
const queue = new Queue([1, 2]);
console.log(queue.entries()); // [1, 2]
queue.enqueue(3);
console.log(queue.entries()); // [1, 2, 3]
queue.dequeue();
console.log(queue.entries()); // [2, 3]
Array.prototype.concat
인수로 전달된 값들(배열 또는 원시값)을 원본 배열의 마지막 요소로 추가한 새로운 배열을 반환한다.
const arr1 = [1, 2];
const arr2 = [3, 4];
// 배열 arr2를 원본 배열 arr1의 마지막 요소로 추가한 새로운 배열을 반환한다.
// 인수로 전달한 값이 배열인 경우 배열을 해체하여 새로운 배열의 요소로 추가한다.
let result = arr1.concat(arr2);
console.log(result); // [1, 2, 3, 4]
// 숫자를 원본 배열 arr1의 마지막 요소로 추가한 새로운 배열을 반환한다.
result = arr1.concat(3);
console.log(result); // [1, 2, 3]
// 배열 arr2와 숫자를 원본 배열 arr1의 마지막 요소로 추가한 새로운 배열을 반환한다.
result = arr1.concat(arr2, 5);
console.log(result); // [1, 2, 3, 4, 5]
// 원본 배열은 변경되지 않는다.
console.log(arr1); // [1, 2]
- push와 unshift 메서드는 concat으로 대체 가능함
- 두 메서드와 유사하게 동작하지만, 미묘한 차이가 있다. 두 메서드는 원본 배열을 직접 변경하지만, concat은 원본배열을 변경하지 않고 새로운 배열을 반환함. 따라서 두 메서드를 사용할 경우 원본 배열을 반드시 변수에 저장해 두어야 한며 concat을 사용할 경우 반환 값을 반드시 변수에 할당받아야 한다.
const arr1 = [3, 4];
// unshift 메서드는 원본 배열을 직접 변경한다.
// 따라서 원본 배열을 변수에 저장해 두지 않으면 변경된 배열을 사용할 수 없다.
arr1.unshift(1, 2);
// unshift 메서드를 사용할 경우 원본 배열을 반드시 변수에 저장해 두어야 결과를 확인할 수 있다.
console.log(arr1); // [1, 2, 3, 4]
// push 메서드는 원본 배열을 직접 변경한다.
// 따라서 원본 배열을 변수에 저장해 두지 않으면 변경된 배열을 사용할 수 없다.
arr1.push(5, 6);
// push 메서드를 사용할 경우 원본 배열을 반드시 변수에 저장해 두어야 결과를 확인할 수 있다.
console.log(arr1); // [1, 2, 3, 4, 5, 6]
// unshift와 push 메서드는 concat 메서드로 대체할 수 있다.
const arr2 = [3, 4];
// concat 메서드는 원본 배열을 변경하지 않고 새로운 배열을 반환한다.
// arr1.unshift(1, 2)를 다음과 같이 대체할 수 있다.
let result = [1, 2].concat(arr2);
console.log(result); // [1, 2, 3, 4]
// arr1.push(5, 6)를 다음과 같이 대체할 수 있다.
result = result.concat(5, 6);
console.log(result); // [1, 2, 3, 4, 5, 6]
- 인수로 전달 받은 값이 배열인 경우 push와 unshift 메서드는 배열을 그대로 원본 배열의 마지막/첫번째 요소로 추가하지만, concat메서드는 인수로 전달받은 배열을 해체하여 새로운 배열의 마지막 요소로 추가함
const arr = [3, 4];
// unshift와 push 메서드는 인수로 전달받은 배열을 그대로 원본 배열의 요소로 추가한다
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]
```
# 27-60
```javascript
let result = [1, 2].concat([3, 4]);
console.log(result); // [1, 2, 3, 4]
// concat 메서드는 ES6의 스프레드 문법으로 대체할 수 있다.
result = [...[1, 2], ...[3, 4]];
console.log(result); // [1, 2, 3, 4]
concat메서드는 ES6부터 스프레드 문법으로 대체할 수 있음
let result = [1, 2].concat([3, 4]);
console.log(result); // [1, 2, 3, 4]
// concat 메서드는 ES6의 스프레드 문법으로 대체할 수 있다.
result = [...[1, 2], ...[3, 4]];
console.log(result); // [1, 2, 3, 4]
- 결론적으로 push/unshift메서드와 concat메서드를 사용하는 대신 ES6의 스프레드 문법을 일관성있게 사용하는 것을 권장함
Array.prototype.splice
원본 배열의 중간에 요소를 추가하거나 중간에 있는 요소를 제거하는 경우 사용한다.
const arr = [1, 2, 3, 4];
// 원본 배열의 인덱스 1부터 2개의 요소를 제거하고 그 자리에 새로운 요소 20, 30을 삽입한다.
const result = arr.splice(1, 2, 20, 30);
// 제거한 요소가 배열로 반환된다.
console.log(result); // [2, 3]
// splice 메서드는 원본 배열을 직접 변경한다.
console.log(arr); // [1, 20, 30, 4]