배열
배열
배열은 객체의 일종이지만, 내부적으로 특별하게 취급되어 일반적이 객체들과는 다른 특징을 갖고 있습니다.
배열 안에 들어있는 값들은 요소(element) 혹은 항목(item)이라고 하며, 순서가 있습니다. 프로토타입으로 Array.prototype 객체가 지정되어 있습니다.
typeof []; // 'object'
배열 리터럴
배열은 배열 리터럴(array literal)을 통해서 생성하는 것이 쉽습니다.
const empty = []; // 빈 배열
const numbers = [1, 2, 3]; // 숫자가 들어있는 배열
const strings = ['one', 'two', 'three']; // 문자열이 들어있는 배열
const objects = [{prop: 1}, {prop: 2}, {prop: 3}]; // 객체가 들어있는 배열
const mixed = [1, 'one', {prop: 1}, null]; // 종합적으로 들어있는 배열
Array 생성자
Array 생성자 를 통해서도 배열을 생성할 수 있습니다. 주어지는 인수에 따라 두 가지 서로 다른 방식으로 동작을 합니다.
- 수 타입의 인수가 하나 주어질 때는 해당 수 만큼의 실이를 갖는 비어있는 배열을 만들어 냅니다.
만약 인수가 양의 정수가 아니라면 에러를 냅니다. - 이 외에 다른 모양으로 인수가 주어지면 그 인수들을 요소로 갖는 배열을 생성합니다.
new Array(1); // [empty]
new Array(1000); // [empty * 1000]
new Array(1, 2, 3); // [1, 2, 3]
Array.of
일관적이지 못한 동작방신 때문에, Array.of 정적 메소드가 추가되었습니다.
Array.of(1, 2, 3)
// `Array.of` 정적 메소드는 인수가 하나이더라도 그 인수를 요소로 갖는 배열을 반환합니다.
Array.of(1); // [1]
// 생성자는 이런 용도로만 사용하세요.
new Array(1000); // [empty * 1000]
Array.from
유사 배열 객체와 iterable이라는 개념이 있어서, 이에 해당하는 값들은 Array.from 정적 메소드를 통해 배열로 쉽게 변환될 수 있습니다.
// `string` 타입은 래퍼 객체를 통해 iterable로 다루어질 수 있습니다.
Array.from('hello'); // ["h", "e", "l", "l", "o"]
요소 읽기
배열의 각 요소는 인덱스(index) 를 이용해 읽어올 수 있으며, 0 이상의 정수 만이 배열의 인덱스가 될 수 있습니다.
첫번째 요소를 가리키는 인덱스는 1이 아니라 0입니다.
const arr = ['one', 'two', 'three'];
arr[0]; // 'one'
arr[1]; // 'two'
arr[2]; // 'three'
arr[3]; // undefined
요소 수정하기
const arr = [false, false, false];
arr[1] = true;
console.log(arr); // [false, true, false]
fill 메소드를 사용하면 많은 요소를 같은 값으로 바꾸어 버릴 수도 있습니다.
const arr = [1, 2, 3, 4, 5];
// 전체를 0으로 채우기
arr.fill(0);
console.log(arr); // [0, 0, 0, 0, 0];
// 인덱스 2와 4 사이를 1로 채우기
arr.fill(1, 2, 4);
console.log(arr); // [0, 0, 1, 1, 0];
Array 생성자와 fill 메소드를 사용하면, 큰 배열을 만들고 값을 채워넣는 일을 쉽게 할 수 있습니다.
new Array(1000); // [empty * 1000]
new Array(1000).fill(5); // [5, 5, 5, 5, ...]
배열의 끝 부분에서 요소를 추가/제거하기
push 메소드와 pop 메소드를 사용하면 배열의 끝 부분에서 요소를 추가하거나 제거할 수 있습니다.
const arr = [];
arr.push('one'); // 1 (요소가 추가된 후의 배열의길이를 반환)
arr.push('two', 'three'); // 3
console.log(arr); // ['one', 'two', 'three']
// 배열의 요소 삭제하기
arr.pop(); // 'three'
arr.pop(); // 'two'
arr.pop(); // 'one'
arr.pop(); // undefined
배열의 시작 부분에서 요소를 추가/제거하기
unshift, shift 메소드를 사용해 배열의 시작 부분에서 요소를 추가하거나 제거할 수도 있습니다.
const arr = [];
arr.unshift(1); // 1 (요소가 추가된 후의 배열의 길이를 반환)
arr.unshift(2, 3); // 3
console.log(arr); [2, 3, 1]
// 배열의 요소 삭제하기
arr.shift(); // 2
arr.shift(); // 3
arr.shift(); // 1
arr.shift(); // undefined
요소를 배열 중간에 삽입하기
splice 메소드를 사용하면 배열의 일부분을 통째로 바꿔버릴 수 있습니다.
또한 배열의 중간 부분에 있는 요소를 제거 하는 데도 활용할 수 있습니다.
let arr = [1, 2, 3, 4, 5];
// 인덱스 `1`인 요소부터 `3`개를 바꿔치기 합니다.
arr.splice(1, 3, 'two', 'three', 'four' ); // [2, 3, 4]
// `splice` 메소드는 바꿔치기를 통해 제거된 요소들을 반환합니다.
console.log(arr); // [1, 'two', 'three', 'four', 5]
const arr = [1, 2, 3, 4, 5];
// 인덱스 `1`인 요소부터 `3`개를 바꿔치기 합니다.
arr.splice(1, 3, 'three'); // [2, 3, 4]
// `3`개를 꺼낸 자리에 하나의 요소를 삽입합니다.
console.log(arr); // [1, 'three', 5]
splice의 뒤쪽 인수를 생략하면, 요소를 제거할 뿐 배열에 아무것도 삽입하지 않습니다.
const arr = [1, 2, 3, 4, 5];
arr.splice(1, 3); // [2, 3, 4]
console.log(arr); // [1, 5]
splice의 두 번째인수로 0을 사용하면, 특정 위치에 여러 요소를 삽입할수도 있습니다.
// 인덱스가 `1`인 요소 앞에 여러 요소를 추가합니다.
const arr = [1, 5];
arr.splice(1, 0, 2, 3, 4); // []
console.log(arr); //[1, 2, 3, 4, 5]
배열 뒤집기
배열의 reverse메소드를 호출하면 해당 배열을 거꾸로 뒤집어 버립니다.
const arr = [1, 2, 3];
// `reverse` 메소드는 `arr`을 뒤집은 후, `arr`을 그대로 반환합니다.
arr.reverse(); // [3, 2, 1]
console.log(arr); // [3, 2, 1]
배열 정렬하기
배열의 sort 메소드를 통해 원하는 방식대로 배열을 정렬할 수 있습니다. sort 메소드의 인수에는 비교 함수를 넘겨주어야 합니다.
비교 함수는 인수 두 개를 받아서, 아래의 조건에 따라 잘 정렬되도록 적절한 값을 반환해주어야 합니다.
배열을 정렬할 때에는 어떤 값을 기준으로 정렬할 지 를 생각해 본 다음, 정렬함수를 만들 때 오름차순으로 정렬할 지, 내림차순으로 정렬할 지를 생각하면 됩니다.
const arr = [3, 1, 4, 5, 2];
// `sort` 메소드는 `arr`을 비교 함수에 따라 정렬한 뒤, `arr`을 그대로 반환합니다.
arr.sort((x, y) => x - y); // [1, 2, 3, 4, 5]
console.log(arr); //[1, 2, 3, 4, 5]
const alphabet = ['a', 'b', 'c', 'd'];
alphabet.sort((x, y) => y.length - x.length);
console.log(alphabet); //['b', 'a', 'd', 'c']
비교 함수를 인수로 넘겨주지 않으면, sort 메소드는 먼저 요소를 전부 문자열로 변환한 후, 유니코드 코드포인트를 비교하는 방식으로 정렬을 합니다. sort 함수를 사용할 때는 꼭 비교함수 를 넘겨줍니다.
[20, 3, 100].sort(); // [100, 20, 3]
['abc', 'def', 'ghr'].sort(); // ['def', 'abc', 'ghr']
[20, 3, 100].sort((x, y) => x - y); // [3, 20, 100]
['abc', 'def', 'ghr'].sort((x, y) => x.localCompare(y)); //[ 'abc', 'ghr', 'def']
배열의 길이
배열의 길이는 length 속성을 통해 쉽게 확인할 수 있습니다.
const arr = [];
console.log(arr.length); // 0
arr.push(1, 2, 3);
console.log(arr.length); // 3
배열 순회하기
- 단순히 배열을 순회하기 위한 목적 for...of 구문 사용, 코드의 간결성이나 속도 측면에서 유리
- 인덱스가필요한 경우 forEach 메소드 사용
- 코드의 실행 속도가 정말로 중요한 부분은 for
for 구문
for루프를 사용해서 배열의 길이만큼 루프를 돌며, 인덱스를 통해 배열의 요소에 접근합니다.
const arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
forEach 메소드
배열의 forEach 메소드를 사용하면, 배열의 각 요소에 대해 함수를 호출할 수 있습니다.
const arr = [1, 2, 3];
arr.forEach(item => {
console.log(`현재 요소 ${item}에 대해 함수가 실행되고 있습니다.`);
});
forEach 메소드에 넘기는 함수에는 총 세 개의 인수가 들어옵니다. 첫 번째는 순회중인 배열의 요소,
두 번재로는 요소의 인덱스, 세 번째로는 순회중인 배열 이 들어옵니다.
const arr = [1, 2, 3];
arr.forEach((item, index, array)=> {
console.log(`현재 ${index + 1}번째 요소에 대해 함수가 실행되고 있습니다.`);
});
for...of 구문
iterable을 순회하기 위해 사용할 수 있습니다.
const arr = [1, 2, 3, 4, 5];
for (let item of arr) {
console.log(item)
}
배열로부터 새로운 값 생성
배열을 순회하는 것만으로도 이런 작업들을 할 수 있지만, 배열에 내장된 메소드를 사용한다면 훨씬 더 간결한 코드로 같은 작업을 할 수 있습니다.
slice
배열의 일부분의 생당하는 새로운 배열을 반환합니다. slice는 얕은 복사(shallow copy) 를 하므로, 배열 안에 배열 또는 객체가 들어있을 때는 주의해서 사용해야 합니다.
const arr = [1, 2, 3, 4, 5];
// 인덱스 2부터 인덱스 4 사이의 요소들을 가지고 새로운 배열을 생성
const newArr = arr.slice(2, 4); // [3, 4]
// newArr을 조작해도, 원본 배열에는 영향을 미치지 않습니다.
newArr[0] = 5;
console.log(newArr); // [5, 4]
console.log(arr); // [1, 2, 3, 4, 5]
map
map메소드는 배열의 각 요소에 함수를 적용해서, 그 반환값을 요소로 갖는 새로운 배열 을 만듭니다.
const arr = [1, 2, 3, 4, 5]
// `arr`의 각 요소를 제곱한 값으로 새 배열을 만듭니다.
const newArr = arr.map(item => item ** 2);
console.log(newArr); // [1, 4, 9, 16, 25]
// 인수로 들어온 함수를 호출할 때 세개의 인수를 넘깁니다.
arr.map((item, index, array) => {
return item * index;
});
// [0, 2, 6, 12, 20]
concat
여러 배열을 연결해서 새 배열을 만들때 사용됩니다.
const arr = [1, 2];
arr.concat([3, 4], [5, 6], [7,8]); // [1, 2, 3, 4, 5, 6, 7, 8]
reduce
모든 요소의 값을 종합해서 하나의 값으로 만드는 계산을 할 때 사용합니다. 또한 초기값은 항상 제공해주는 것이 좋습니다.
배열에 대한 계산을 하는 만능 도구
const arr = [1, 2, 3]
arr.reduce((acc, item) => acc + item, 0); // 6
위 코드에서 일어난 일을 순서대로 정리
- 초기값 0과 배열의 첫번째 요소인 1을 인수로 해서 함수를 호출합니다. 즉, acc 매개변수에 0이 대입되고, item매개변수에 1이 대입됩니다. 결과값은 1이 되며 이 값을 누적값(accumulator) 라고 부릅니다
- 누적값 1과 배열의 두번째 요소인 2를 인수로 해서 함수를 호출합니다. 결과값이 3이 다시 누적갓이 됩니다.
- 누적값 3과 배열의 세번째 요소인 3을 인수로 해서 함수를 호출합니다. 결과값은 6입니다
- 더 이성 요소가 남아있지 않으므로 reduce 호출의 결과값은 6이 됩니다.
filter
배열에서 원하는 요소만을 골라내어 새로운 배열을 생성할 수 있습니다. 또한 진리값(boolean)을 반환하는 함수를 주어야 합니다.
const arr = [1, 2, 3, 4, 5];
// 짝수만 골라내기
arr.filter(item => item % 2 === 0); // [2, 4];
join
배열의 요소들을 문자열로 변환 후, 메소드에 주어진 구분자(separstor)를 이용해 하나의 문자열로 결합하여 반환합니다.
const arr = [1, 2, 3]
arr.join('&'); // '1&2&3'
요소 찾기
indexOf & lastIndexOf 메소드
메소드를 사용하면 특정 요소가 배열의 몇 번째에 위치하는 지를 알게 됨
일치하는 요소가 없다면, 두 메소드 모두 -1을 반환
- indexOf : 배열의 왼쪽부터 처음 만나는 요소의 인덱스 반환
- lastIndexOf : 배열의의 오른쪽부터 처음 만나는 요소의 인덱스 반환
const arr = ['a', 'b', 'c', 'b', 'a'];
arr.indexOf('b'); // 1
arr.lastIndexOf('b'); // 3
arr.indexOf('z'); // -1
arr.lastIndexOf('z'); // -1
find 메소드 & findIndex
메소드 특정 조건을 만족하는 요소를 찾으며, 왼쪽부터 검사해서 처음 만나는 요소를 찾음
- find : 요소 자체를 반환. 조건을 만족하는 요소를 찾지 못하면, undefined 반환
- findIndex : 요소의 인덱스를 반환. 조건을 만족하는 요소를 찾지 못하면, -1 반환
const names = ['Denton', 'Kathleen', 'Ebba', 'Bruce'];
names.find(item => item.length < 6); // 'Ebba'
names.findIndex(item => item.length < 6); // 2
names.find(item => item.length > 8); // undefined
names.findIndex(item => item.length > 8); // -1
배열 특정 조건 만족하는지 판별
배열이 특정 조건을 만족하는지를 나타내는 진리값을 반환
- includes : 배열이 특정 요소를 포함하고 있는지를 판별. includes가 진리값을 반환
- every : predicate을 인수로 받아서, 모든 요소가 조건을 만족하는 지를 검사
- some : predicate을 인수로 받아서, 조건을 만족하는 요소가 하나라도 있는지 검사
// includes
const arr = ['one', 'two', 'three'];
arr.includes('one'); // true
arr.includes('one', 1); // false
// every
const arr = ['one', 'two', 'three'];
arr.every(item => item.length > 2); // true
arr.every(item => item.length > 3); // false
// some
const arr = ['one', 'two', 'three'];
arr.some(item => item.length > 3); // true
arr.some(item => item.length > 5); // false
배열인지 판별
Array.isArray
Array.isArray([]); // true
Array.isArray({}); // false
Array.isArray('hello'); // false