자바스크립트에서 배열은 독립된 자료형으로 취급되지 않고 객체형에 속한다. 정확히는 숫자값을 키로 가지는 객체이다. 따라서 typeof []
결과 object가 반환되며, 배열인지 아닌지 판단하기 위해서는 Array.isArray(value)
를 사용해야 한다.
const arr1 = new Array();
const arr2 = [1, 2]; // 주로 쓰이는 방법
console.log(arr2[0]); // 첫번째 인덱스에 접근
console.log(arr2[arr2.length-1]); // 마지막 인덱스에 접근
반복문을 통한 배열의 요소 출력 (단순 반복시 권장)
for
for (let i = 0; i < arr2.length; i++) {
console.log(arr2[i]);
}
for in
배열의 인덱스를 반환한다(객체의 경우 프로퍼티 이름 반환). 배열에서 사용할 경우 배열 요소들만 순회하지 않는다. 배열은 순서를 보장하는 데이터 구조이므로 사용하지 않는 것이 좋다. 대신 ES6에 추가된 for of
문을 사용한다.
for (let index in arr2) {
console.log(arr2[index])
}
arr2.name = 'wonil;
for (let index in arr2) {
console.log(arr2[index]) // 따로 추가한 name의 값인 'wonil'도 출력
}
for of
배열은 이터러블 프로토콜을 따른다.
for (let element of arr2) {
console.log(element);
}
forEach
배열명.forEach()
형태로 사용할 수 있으며, 배열 순회 시 권장되는 방법이다. 새로운 배열을 반환받아야 할 경우에는 map()
을 사용함에 유의한다.
arr2.forEach(function (element, index) {
console.log(element, index);
}
// Arrow Function 활용
arr2.forEach((element, index) => console.log(element, index));
indexOf()
arr.indexOf(item, from)
: 인덱스 from부터 시작해 item을 찾아 요소의 인덱스를 출력한다.
arr.indexOf(2); // 발견하면 해당 요소의 인덱스를 반환, 발견하지 못하면 -1
lastIndexOf()
검색하려는 요소 중 끝에서 가장 가까운 요소의 인덱스를 출력
includes()
arr.includes(item, from)
: 인덱스 from부터 시작해 item이 있는지를 검색하며, 해당 요소를 발견하면 true를 반환한다. (요소 포함 여부 확인)
arr.includes(3);
indexOf/lastIndexOf vs includes
indexOf/lastIndexOf
,includes
모두 완전 항등 연산자(===)를 사용하지만,includes
는NaN
도 제대로 처리한다는 점에서 약간의 차이가 있다.
배열에 요소를 추가 또는 제거
push()
& pop()
배열의 맨끝에 요소를 추가 또는 제거한다.
arr2.push(3);
item= arr2.pop(); // 제거 후 반환
unshift()
& shift()
배열의 맨처음에 요소를 추가 또는 맨처음 요소를 제거하며, 배열의 모든 인덱스를 뒤로 미루기 때문에 push(), pop()보다 느리다.
// unshift()
arr2.unshift(0); // arr2 = [0, 1, 2]
// shift()
arr2.shift(); // arr2 = [2]
splice()
배열 내 지정된 요소를 제거(원본 배열을 변경)하며, 반환값은 삭제된 요소로 구성된 배열이 된다.
splice(start: number, deleteCount?: number): number[];
arr2.splice(0); // 인덱스 0 이후(인덱스 0인 요소포함)의 모든 요소를 지움
arr2.splice(0, 1); // 인덱스 0 이후의 1개 요소를 지움
arr2.splice(0, 1, 3, 4); // 요소를 지운 자리에 새로운 요소 추가
arr2.splice(0, 0, 3, 4); // 요소를 지우지 않고 새로운 요소 추가
arr2.splice(-1, 0, 3, 4); // 배열 끝에서부터 3[-3], 4[-2] 요소 추가
findIndex()
으로 먼저 넘어온 id와 동일한 id의 요소의 인덱스 이후의 1개 요소를 지우게 된다. fileInfo.splice(state.fileInfo.findIndex((file) => file.id === action.payload), 1);
splice vs delete
delete는 obj.key를 삭제하므로 해당 키에 상응하는 값을 지우게 된다. 이는 해당 키의 값을 삭제한 것이므로 해당 키의 값은 빈 공간으로 남아있게 된다. 따라서 배열의 길이는 삭제 후에도 감소하지 않는다.
concat()
기존 배열의 요소를 사용해 새로운 배열을 만들거나 기존 배열에 요소를 추가하여 새로운 배열을 반환한다. 인수에는 배열이나 값이 올 수 있으며, 인수가 배열인 경우 배열의 모든 요소가 복사되고, 단순 값인 경우는 인수가 그대로 복사된다.
// 리액트: 기존 앨범 사진들에 api 호출로부터 불러온 새로운 사진들을 추가하는 코드
setAlbums((prev) => prev.concat(albums));
// 리액트: 출석 정보 슬라이드 배너 생성을 위해 주간, 월간 출석 순위배열을 합침
setAttdRankSlides(weeklyAttdRank.concat(monthlyAttdRank))
Symbol.isConcatSpreadable
concat
메서드는 제공받은 배열의 요소를 복사해 활용한다. 이때, 객체가 인자로 넘어오는 경우에 해당 객체는 분해되지 않고 통으로 복사되어 더해진다.let arr = [3, "wonil"]; let arrayLike = { 0: "arg", length: 1 }; alert( arr.concat(arrayLike) ); // 3, wonil, [object Object]
유사 배열 객체에 특수한 프로퍼티
Symbol.isConcatSpreadable
이 존재하면concat
은 이 객체를 배열처럼 취급하여 객체 프로퍼티 값을 더하는 동작이 가능해진다.let arrayLike = { 0: "arg1", 1: "arg2":, [Symbol.isConcatSpreadable]: true, length: 2 }; alert( arr.concat(arrayLike) ); // 3, wonil, arg1, arg2
Array.from()
유사 배열 객체나 반복 가능한 객체를 얕게 복사해 새로운 배열 객체를 생성한다.
전개구문(spread syntax)은 배열 요소를 수동으로 나열하는 불편함을 덜고자 등장하였으며, ...
를 사용하기 때문에 나머지 매개변수와 비슷해 보이지만 나머지 매개변수와 반대 역할을 한다.
배열이 아니더라도 이터러블 객체이면 전개 구문을 사용할 수 있다. 다만, 유사 배열 객체에는 사용될 수 없다. ⇒ 무언가를 배열로 바꿀 때는 전개 구문보다
Array.from
을 보편적으로 사용된다.
요소의 포인터가 가리키는 주소의 참조값만 가져오므로, 원래값을 변경하면 복사한 것도 변경된다.
const obj1= {key: 'key1'};
const obj2= {key: 'key2'};
const array= [obj1, obj2];
// array copy
const arrayCopy= [...array]; // array와 같음
const arrayCopy2= [...array, {key: 'key3'}]; // 배열요소 추가
const num1= [1, 2];
const num2= [3, 4];
const num = [...num1, ...num2];
console.log(num); // [1, 2, 3, 4] (concatenation)
join()
배열을 문자열로 변환
join(separator?: string): string;
const fruits= ['apple', 'banana', 'orange'];
const result= fruits.join(); // 구분자에 아무것도 지정하지 않는 경우
console.log(result); // apple, banana, orange (기본값으로 ,(콤마)로 지정)
split()
지정한 구분자(delimiter)를 기준으로 문자열을 나누어 배열로 변환
split(separator: string|RegExp, limit?: number): string[];
const fruits= 'apple banana orange';
const result= fruits.split(' ');
console.log(result); // [apple, banana, orange]
number 인자
두 번째 인자로 넘어오는 숫자로 배열의 길이를 제한할 수 있다.
reverse()
배열을 뒤집어서 반환
reverse(): T[];
const array = [1, 2, 3, 4, 5];
const result = array.reverse();
console.log(result); // [5, 4, 3, 2, 1]
console.log(array); // [5, 4, 3, 2, 1] (변환된 배열을 반환)
slice()
배열을 주어진 구간만큼 잘라 새로운 배열로 반환한다. end의 인덱스는 배제됨에 유의한다.
slice(start?: number, end?: number): T[];
slice()
slice(start)
slice(start, end)
얕은 복사
매개변수가 없을 경우, 원본 배열의 얕은 복사를 수행한다.
const array= [1, 2, 3, 4, 5];
const result = array.slice(2, 5); // [3, 4, 5]
const result2 = array.slice(-2); // [4, 5]
console.log(array); // [1, 2, 3, 4, 5]
arr.slice() vs str.slice()
각각 반환 값이 배열, 문자열이라는 점에서 차이를 보인다.
// 넘겨준 size만큼의 인생네컷 이미지를 가져오는 목업 데이터
const life4CutImages = life4CutData["life-4-cut"].slice(0, size);
const students= [
new Student('A', 29, true, 45),
new Student('B', 28, false, 80),
new Student('C', 30, true, 90),
new Student('D', 40, false, 66),
new Student('E', 18, true, 88)
];
find()
주어진 판별 함수를 만족하는 첫 번째 요소의 값을 반환하며, 만족하는 요소가 없다면 undefined를 반환한다.
find는 순차 탐색을 사용하기 때문에, 이를 활용하여 두 데이터에 대한 비교를 하는 경우 데이터의 크기를 고려하여 가장 최선의 방법인지 생각할 필요가 있다.
find<S extends T>(predicate: (this: void, value: T, index: number, obj: T[]) => value is S, thisArg?: any): S | undefined;
find(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): T | undefined;
const result= students.find(function(student, index){
console.log(student, index); // students 배열내 각 요소와 그 요소의 인덱스를 모두 출력
});
let users = [
{id: 1, name: "John"},
{id: 2, name: "Wonil"},
{id: 3, name: "Gildong"}
];
let user = users.find(item => item.id == 1);
alert(user.name); // John
function isPrime(element, index, array) {
const start = 2;
while (start <= Math.sqrt(element)) {
if (element % start++ < 1) {
return false;
}
}
return element > 1;
}
console.log([4, 6, 8, 12].find(isPrime)); // undefined, not found
console.log([4, 5, 8, 12].find(isPrime)); // 5
findIndex()
주어진 판별 함수를 만족하는 배열의 첫 번째 요소에 대한 인덱스를 반환하며, 만족하는 요소가 없으면 -1을 반환
Array.prototype.findIndex() - JavaScript | MDN
state.fileInfo.splice(state.fileInfo.findIndex((file) => file.id === action.payload), 1);
filter()
주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환
filter<S extends T>(predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[];
filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[];
const result= students.filter((student)=> student.enrolled);
console.log(result); // students배열내 enrolled값이 true인 요소들만 출력
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
find vs filter
find
는 함수의 반환 값을 true로 만드는 단 하나의 요소를 찾는 반면,filter
는 전체 요소를 담은 배열을 반환한다.
map()
배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열로 반환 (배열 순회 후 새로운 배열을 얻고 싶을 시 권장)
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
Array.prototype.map() - JavaScript | MDN
const result= students.map((student)=> student.score);
console.log(result); // [45, 80, 90, 66, 88]
some()
배열 안의 어떤 요소라도 주어진 판별 함수를 통과하는지 판단하여 불리언을 반환한다.
forEach
에서 break 문을 지원하지 않으므로 해당 기능을 some() 으로 대체할 수 있다.
some(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
Array.prototype.some() - JavaScript | MDN
isValidMenuName(menuName) {
return MENU_INFO.some((menu) => menu.name === menuName);
}
every()
배열 안의 모든 요소가 주어진 판별 함수를 통과하는지 판단하여 불리언을 반환한다.
every(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
const result= students.every((student)=> student.score < 50);
console.log(result); // false
reduce()
배열의 각 요소에 대해 주어진 리듀서(reducer) 함수를 실행하고 하나의 결과값을 반환 (배열을 기반으로 값 하나를 도출할 때 사용된다.)
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
이전 함수 호출 결과는 다음 함수 호출시의 첫 번째 인수로 사용된다.
초깃값을 생략할 수 있는데, 이 때는 배열의 첫 번째 요소를 초깃값으로 사용하고 두 번째 요소부터 함수를 호출한다. (빈 배열시 에러가 발생하므로 유의)
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((sum, current) => sum + current, 0);
alert(result); // 15
calculateTotalPrice() {
let totalPrice = 0;
this.#items.reduce(
(acc, item) => acc + item.price * item.quantity,
0
);
}
reduceRight
reduce
와 동일한 기능을 하지만 배열의 마지막부터 연산을 수행한다는 점이 다르다.
sort
배열의 요소를 적절한 위치에 정렬하며, 배열 자체가 변경된다.(기본 정렬순서는 유니코드 코드포인트순)
일시적으로 숫자 타입을 문자열(String) 타입을 형변환
sort(compareFn?: (a: T, b: T) => number): this;
const result= students
.map((student) => student.score)
.sort((a, b) => b - a) // 내림차순 정렬
// 위 코드를 풀어 쓴 것
// sort((a, b) => {
// return b - a;
// })
.join();
console.log(result); // 90, 88, 80, 66, 45
fill()
배열의 인덱스 범위 내에 있는 모든 요소를 정적 값으로 변경하며, 새로운 배열을 반환한다.
dp = Array(N).fill(0); // N 길이의 배열에 0을 채운다.
2차원 배열 다루기
const dp=Array.from(Array(n),()=>Array(m).fill(0)); // 가로m, 세로n의 2차원 배열