- 한방에 자바스크립트 배열 마스터하기(뽀개기)! 😎
한 여름입니다! 🏖️
드디어 코딩 실력 좀 올려볼까요? 🤔
이번엔 자바스크립트에서 절대 빼놓을 수 없는 배열 뽀개기에 도전해봅시다! 💪
자주 쓰는 배열 조작법부터 최신 기술까지, 알차게 준비했으니까 🤓 이번 방학에 배열 마스터하고 코딩 고수로 레벨업! 🚀
이 글을 읽으면 뭐가 좋냐고요? 😉
맨날 쓰던 for
문 말고, 더 힙한 배열 조작법 득템! 😎
면접 때 "배열 잘 다루세요?" 질문에도 자신있게 대답 가능! 👨💻👩💻
팀플 과제 할 때 센스있는 코딩으로 팀원들에게 인정받기! 😏
배열 만들기 101
배열은 여러 값을 한 묶음으로 다루는 데이터 구조입니다. 자바스크립트에서는 일종의 객체로 취급됩니다. 🤔
배열 만드는 방법은 여러가지가 있습니다. 다 같은 결과지만, 미묘한 차이가 있으니까 잘 봐두세요! 👀
// 셋 다 똑같은 배열이 만들어집니다!
new Array("Alice", "Bob");
Array("Alice", "Bob");
Array.of("Alice", "Bob");
["Alice", "Bob"]; // 👈 이게 제일 깔끔하고 많이 쓰는 방법입니다!
new Array()
(또는 Array()
)랑 Array.of()
는 인수로 숫자 하나만 넣었을 때 동작이 다릅니다.
new Array()
는 그 길이만큼 빈 배열을 만들고, Array.of()
는 그 숫자 하나만 들어있는 배열을 만듭니다.
new Array(3); // [빈 요소 × 3] (길이 3)
Array.of(3); // [3] (길이 1)
배열에는 null
이나 undefined
도 아닌, 빈 슬롯이라는 게 존재합니다. 👻
new Array(3)
처럼 길이만 정해서 배열 만들 때- 배열 길이보다 큰 인덱스에 값 넣을 때
arr.length
에 숫자 넣어서 배열 길이 늘릴 때
const arr = ["a", "b"];
arr[4] = "e"; // ["a", "b", 빈 요소 × 2, "e"]
arr.length = 7; // ["a", "b", 빈 요소 × 2, "e", 빈 요소 × 2]
빈 슬롯은 배열 메서드마다 다르게 처리되니까 조심! ⚠️ 어떤 메서드는 무시하고, 어떤 메서드는 undefined
로 취급합니다. 🤯 웬만하면 빈 슬롯 안 생기게 코딩하는 게 좋습니다! 👍
Array.from()
은 배열을 좀 더 유연하게 만들 수 있는 꿀팁입니다! 😉 문자열이나 NodeList 같은 걸 배열로 바꿔줍니다.
// 문자열 넣으면 한 글자씩 쪼개서 배열로 만들어줍니다!
Array.from('test'); // ["t", "e", "s", "t"]
// NodeList도 배열로 뿅! ✨
// (HTML에 `<input name="test">`가 두 개 있을 때)
Array.from(document.getElementsByName("test")); // [input, input]
// 길이만 있는 객체 넣으면 그 길이만큼 빈 배열 생성!
Array.from({ length: 3 }); // [undefined, undefined, undefined]
// 함수 넣어서 커스텀 배열 만들기! 🛠️
Array.from([1, 2, 3], (e, i) => {
// e는 각 요소, i는 인덱스 (0부터 시작!)
return e + i;
}); // [1, 3, 5]
Array.fromAsync()
는 최신 기술입니다! ⚡️
Promise가 들어있는 배열이나 AsyncGenerator 같은 비동기 데이터도 배열로 만들 수 있습니다. 😎
// 1초 뒤에 숫자 반환하는 함수
const wait1sec = async (num) => {
await new Promise(resolve => setTimeout(resolve, 1000));
return num;
}
await Array.fromAsync([
wait1sec(1),
wait1sec(2),
wait1sec(3),
], (e, i) => {
return e + i;
}); // (1초 후에) [1, 3, 5]
// 비동기 제너레이터 함수
async function* getAsyncGenerator() {
yield await wait1sec(1);
yield await wait1sec(2);
yield await wait1sec(3);
}
// 비동기 제너레이터로 배열 만들기!
await Array.fromAsync(getAsyncGenerator()); // (3초 후에) [1, 2, 3]
배열로 루프 돌리기 🎠
기본적인 방법부터 배열 자체 기능까지, 다양한 방법이 있습니다!
const arr = ["a", "b", "c"];
// 전통적인 for문
for (let i = 0; i < arr.length; i++) {
arr[i]; // 각 요소에 접근!
}
// for...in 문 (인덱스로 접근)
for (const i in arr) {
i; // "0", "1", "2" (문자열!)
arr[i]; // 각 요소에 접근!
}
// for...of 문 (요소로 접근)
for (const e of arr) {
e; // 각 요소에 접근!
}
// forEach 메서드
arr.forEach((e) => {
e; // 각 요소에 접근!
});
for(;;)
에서는 const
못 쓰지만, for(in)
이랑 for(of)
에서는 쓸 수 있습니다! 😉
for
문의 continue
는 arr.forEach()
에서 return
으로 대체 가능!
하지만 break
는 arr.forEach()
에서 못 씁니다! 🙅♀️
const arr = ["a", "b", "c"];
for(const e of arr) {
if (e === "a") continue; // 다음 요소로!
if (e === "b") break; // for문 탈출!
}
arr.forEach((e) => {
if (e === "a") return; // 다음 요소로!
// break는 못 써! 😭
});
배열 조작하기 🛠️
배열에는 다양한 조작 기능이 있습니다! 유용한 것들만 쏙쏙 골라봤습니다! 😉
요소 하나만 쏙! 뽑아내기
const arr = ["a", "b", "c", "d"];
// 전통적인 인덱스 접근 방식
arr[0]; // "a"
arr[-1]; // undefined
arr[arr.length - 1]; // "d" (마지막 요소!)
// at 메서드 (음수 인덱스 지원!)
arr.at(0); // "a"
arr.at(-1); // "d" (마지막 요소!)
// find 메서드 (조건에 맞는 첫 번째 요소 찾기)
arr.find(e => e === "b" || e === "c"); // "b"
arr.find(e => e === "e"); // undefined
// shift 메서드 (첫 번째 요소 뽑아내기)
arr.shift(); // "a"
arr; // ["b", "c", "d"] (배열 자체가 바뀜! ⚠️)
// pop 메서드 (마지막 요소 뽑아내기)
arr.pop(); // "d"
arr; // ["b", "c"] (배열 자체가 바뀜! ⚠️)
마지막 요소 뽑아낼 땐 arr[arr.length - 1]
보다 arr.at(-1)
이 훨씬 간편합니다! 😎
arr.shift()
랑 arr.pop()
은 배열 자체를 바꾼다는 거 잊지마세요! 😱
이런 메서드를 파괴적 메서드라고 부릅니다. 배열 조작할 때는 파괴적인지 아닌지 꼭 확인해야 합니다! 🧐
⚠️ 주의!
arr.shift()
랑arr.pop()
뿐만 아니라, 다른 파괴적 메서드들도const
로 선언된 배열을 바꿀 수 있습니다! 🤯
arr.find()
는 복잡한 조건으로 요소 찾을 때 유용합니다!
여러 개가 조건에 맞으면 첫 번째 것만 반환합니다. 최근에는 arr.findLast()
도 나왔는데, 얘는 마지막에 찾은 걸 반환합니다! 😉
const arr = ["a", "b", "c", "d"];
arr.findLast(e => e === "b" || e === "c"); // "c"
arr.findLast(e => e === "e"); // undefined
요소 여러 개 뽑아내기
const arr = ["a", "b", "c", "d"];
// slice 메서드 (범위 지정해서 뽑아내기)
arr.slice(1, 3); // ["b", "c"]
arr.slice(-3, -1); // ["b", "c"] (뒤에서부터 셀 수도 있습니다!)
// filter 메서드 (조건에 맞는 요소들만 뽑아내기)
arr.filter(e => e === "b" || e === "c"); // ["b", "c"]
arr.filter(e => e === "e"); // []
arr.slice()
는 범위를 직접 지정하고, arr.filter()
는 조건으로 뽑아내는 게 다릅니다!
arr.slice()
는 첫 번째 인수가 시작, 두 번째 인수가 끝입니다.
둘 다 0부터 시작하는 인덱스! 😉 근데 두 번째 인수로 지정한 끝점은 포함 안 됩니다!🙅♀️
음수 값 넣으면 arr.at()
처럼 동작합니다! 예를 들어 -1은 마지막 요소입니다!
요소 찾기 🔍
지금까지 본 것도 "찾기"지만, 요소가 있는지, 어디 있는지 확인하는 메서드도 있습니다!
const arr = ["a", "b", "c", "d"];
// includes 메서드 (값이 있는지 확인)
arr.includes("c"); // true
arr.includes("e"); // false
// some 메서드 (조건에 맞는 요소가 있는지 확인)
arr.some(e => e === "c"); // true
arr.some(e => e === "e"); // false
// every 메서드 (모든 요소가 조건에 맞는지 확인)
arr.every(e => typeof e === "string"); // true
arr.every(e => e === "c"); // false
arr.includes()
는 값이 있는지 확인하고, arr.some()
은 조건으로 확인합니다!
arr.every()
는 모든 요소가 조건에 맞아야 true
를 반환합니다!
// arr[1]이랑 arr[2]가 둘 다 "o"입니다!
const arr = ["f", "o", "o", "d"];
// indexOf 메서드 (처음 찾은 요소의 인덱스)
arr.indexOf("o"); // 1
arr.indexOf("e"); // -1 (없으면 -1!)
// lastIndexOf 메서드 (마지막에 찾은 요소의 인덱스)
arr.lastIndexOf("o"); // 2
arr.lastIndexOf("e"); // -1 (없으면 -1!)
arr.indexOf
는 찾은 값의 첫 번째 인덱스를 반환하고, 없으면 -1을 반환합니다! (주의! false
가 아닙니다! 🙅♀️)
arr.lastIndexOf
는 마지막에 찾은 값의 인덱스를 반환합니다!
요소 값으로 꽉꽉 채우기
const arr = [1, null, 3];
arr.fill(0); // [0, 0, 0]
Array(5).fill(0); // [0, 0, 0, 0, 0] (초기화할 때 편합니다!)
arr.fill()
은 배열을 특정 값으로 채워줍니다! 배열 자체를 바꾸고, 바뀐 배열을 반환합니다!
arr.fill()
은 두 번째, 세 번째 인수로 범위를 지정할 수도 있습니다! arr.slice()
랑 비슷하게 동작합니다! 😉
const arr = [1, null, 3, 4, 5];
arr.fill(0, 1, 3); // [1, 0, 0, 4, 5]
배열 붙이기 🔗
const arr1 = ["a", "b"];
const arr2 = ["c", "d"];
// 스프레드 구문 (펼쳐서 붙이기!)
[...arr1, ...arr2]; // ["a", "b", "c", "d"]
[...arr1, "e"]; // ["a", "b", "e"]
["e", ...arr1]; // ["e", "a", "b"]
// concat 메서드 (붙이기!)
arr1.concat(arr2); // ["a", "b", "c", "d"]
arr1.concat("e"); // ["a", "b", "e"]
arr1.concat(arr2, "e"); // ["a", "b", "c", "d", "e"] (여러 개 붙일 수 있습니다!)
// unshift 메서드 (앞에 추가!)
arr1.unshift("e", "f"); // 4 (새로운 길이 반환!)
arr1; // ["e", "f", "a", "b"]
// push 메서드 (뒤에 추가!)
arr1.push("g", "h"); // 6 (새로운 길이 반환!)
arr1; // ["e", "f", "a", "b", "g", "h"]
arr.concat()
도 좋지만, 비배열 뒤에 배열 붙일 땐 스프레드 구문이 더 편합니다! 😉
arr.unshift()
랑 arr.push()
는 배열 자체를 바꾼다는 거 잊지마세요! 😱
arr.shift()
랑 arr.pop()
이랑 비슷하지만, 반환값은 바뀐 배열의 길이입니다!
그리고 여기 나온 메서드들은 인수를 여러 개 넣을 수 있어서, 사실상 무한대로 붙일 수 있습니다! ♾️
배열 정렬하기 🧹
오름차순 (작은 순서대로)
const arr1 = [3, 2, 4, 1];
const arr2 = [
{ name: "Alice", salary: 200_000 }, // 단위: 달러
{ name: "Bob", salary: 180_000 }, // 단위: 달러
{ name: "Charlie", salary: 250_000 }, // 단위: 달러
];
// sort 메서드 (오름차순 정렬)
arr1.sort(); // [1, 2, 3, 4]
arr1; // [1, 2, 3, 4] (배열 자체가 바뀜! ⚠️)
arr2.sort((a, b) => a.salary - b.salary);
// [
// { name: "Bob", salary: 180_000 },
// { name: "Alice", salary: 200_000 },
// { name: "Charlie", salary: 250_000 },
// ] (조건 지정해서 정렬 가능!)
const arr3 = [3, 2, 4, 1];
// toSorted() 메서드 (오름차순 정렬, 비파괴적!)
arr3.toSorted(); // [1, 2, 3, 4]
arr3; // [3, 2, 4, 1] (원래 배열은 그대로!)
arr.sort()
가 전통적인 방법이지만, 배열 자체를 바꾼다는 거 잊지마세요! 😱 반환값은 arr
이랑 똑같습니다!
원래 배열은 그대로 두고 싶을 땐 최신 기술인 arr.toSorted()
를 쓰세요! 😎
arr.sort()
랑 똑같이 쓰면 되는데, 원래 배열은 안 바꾸고 정렬된 새 배열을 반환합니다!
이렇게 원래 배열 안 바꾸는 메서드를 비파괴적 메서드라고 부릅니다.
최근에 배열 조작에 비파괴적 메서드가 많이 추가되었습니다! 😉 뒤에 나오는 arr.to***()
메서드들도 그 중 하나입니다!
arr.sort()
랑 arr.toSorted()
둘 다 인수로 정렬 조건을 나타내는 함수를 넣을 수 있습니다!
객체 정렬할 때 유용합니다! 😉 위에 예시가 있지만, 간단히 설명하면 이런 함수를 만들어야 합니다!
- 비교할 두 개의 인수를 가진 함수 (a랑 b)
- 다음 숫자 중 하나를 반환:
- a가 더 크면 양수
- a가 더 작으면 음수
- 둘이 같으면 0
내림차순 (큰 순서대로)
const arr1 = [3, 2, 4, 1];
// reverse 메서드 (내림차순 정렬)
arr1.reverse(); // [1, 4, 2, 3]
arr1; // [1, 4, 2, 3] (배열 자체가 바뀜! ⚠️)
const arr2 = [3, 2, 4, 1];
// toReversed 메서드 (내림차순 정렬, 비파괴적!)
arr2.toReversed(); // [1, 4, 2, 3]
arr2; // [3, 2, 4, 1] (원래 배열은 그대로!)
reverse()
메서드도 전통적인 방법이지만, 배열 자체를 바꾼다는 거 잊지마세요! 😱
반환값은 arr
이랑 똑같습니다!
원래 배열은 그대로 두고 싶을 땐 최신 기술인 arr.toReversed()
를 쓰세요! 😎
arr.toSorted()
처럼 원래 배열은 안 바꾸고 정렬된 새 배열을 반환합니다!
그리고 sort()
(또는 toSorted()
)랑 reverse()
(또는 toReversed()
)를 조합하면 내림차순으로 정렬할 수 있습니다!
const arr = [3, 2, 4, 1];
arr.toSorted().toReversed(); // [4, 3, 2, 1]
// 숫자 배열이면 이렇게도 가능합니다!
arr.toSorted((a, b) => b - a); // [4, 3, 2, 1]
유연하게 가공하기 🎨
const arr1 = [3, 2, 4, 1];
// map 메서드 (각 요소를 함수 결과로 변경)
arr1.map(e => e * 2); // [6, 4, 8, 2]
// splice 메서드 (요소 제거/대체)
arr1.splice(1, 2, "a", "b"); // [2, 4] (제거된 요소 배열)
arr1; // [3, "a", "b", 1] (배열 자체가 바뀜! ⚠️)
const arr2 = [3, 2, 4, 1];
// toSpliced 메서드 (요소 제거/대체, 비파괴적!)
arr2.toSpliced(1, 2, "a", "b"); // [3, "a", "b", 1] (가공된 배열)
arr2; // [3, 2, 4, 1] (원래 배열은 그대로!)
arr.map()
은 각 요소를 함수 결과로 바꾼 새 배열을 반환합니다! 모든 요소를 원하는대로 바꿀 수 있어서 엄청 유연합니다! 😎
arr.splice()
는 요소를 제거하거나 대체할 수 있습니다!
첫 번째 인수는 시작 위치, 두 번째 인수는 제거할 개수, 세 번째 인수부터는 새로 넣을 값들입니다!
첫 번째 인수에 음수 넣으면 arr.at()
처럼 동작합니다! 예를 들어 -1은 마지막 요소입니다!
반환값은 제거된 요소 배열이고, arr
자체가 바뀐다는 거 잊지마세요! 😱
최근에 나온 arr.toSpliced()
는 가공된 배열을 반환하고, arr
자체는 안 바꿉니다! 😉
중첩 배열 펴기
const arr1 = [
[10, 11, 12],
[20, 21],
[30],
];
const arr2 = [
[[10, 11], 12], // 더 깊은 중첩!
[20, 21],
[30],
];
// flat 메서드 (중첩 배열 펴기)
arr1.flat(); // [10, 11, 12, 20, 21, 30]
arr2.flat(); // [[10, 11], 12, 20, 21, 30] (한 번만 펴짐)
arr2.flat(2); // [10, 11, 12, 20, 21, 30] (인수 넣으면 더 깊이 펴짐!)
const arr3 = [1, 2, 3];
// flatMap 메서드 (map 후 flat)
arr3.flatMap(e => e > 1 ? [e, 0] : 9); // [9, 2, 0, 3, 0]
// flatMap은 이렇게 쓸 수도 있습니다!
arr3.map(e => e > 1 ? [e, 0] : 9).flat(); // [9, 2, 0, 3, 0]
arr.flat()
은 중첩 배열을 펴줍니다!
인수로 넣은 숫자만큼 펴고, 안 넣으면 한 번만 폅니다! 중첩이 두 단계 이상이면, 두 단계 이후는 그대로 중첩된 상태로 남습니다!
arr.flatMap()
은 arr.map().flat()
이랑 똑같습니다! 먼저 arr.map()
하고 그 결과를 flat()
하는 겁니다!
arr
에 중첩이 없다는 걸 알면, 조건에 따라 빈 배열 반환하는 함수를 flatMap()
에 넣어서 arr.filter()
처럼 쓸 수도 있습니다! 😉
TypeScript에서 arr.filter()
때문에 타입 에러 났을 때 유용했습니다!
const arr = [1, null, 3];
arr.flatMap(e => {
if (e === null) return []; // 제거하고 싶은 요소는 빈 배열 반환!
return e * 2;
}); // [2, 6] (필터링 + 가공 동시에!)
배열을 하나의 값으로 뭉치기!
const arr1 = [1, 2, 3, 4];
const arr2 = ["a", null, "c", "d"];
// reduce 메서드 (배열을 하나의 값으로 축약)
arr1.reduce((prev, cur) => prev + cur, 0); // 10
arr2.reduce((prev, cur) => prev + "-" + cur); // "a-null-c-d"
// join 메서드 (배열을 문자열로 합치기)
arr2.join("-"); // "a--c-d"
arr.reduce()
는 처음 두 요소를 함수에 넣고, 그 결과랑 세 번째 요소를 다시 함수에 넣고... 이걸 모든 요소에 대해 반복해서 하나의 값을 만들어냅니다!
위에 arr1
예시는 모든 요소의 합을 구하는 것입니다! 두 번째 인수에 초기값을 넣을 수 있습니다!
그러면 처음에 초기값이랑 첫 번째 요소가 함수에 들어갑니다!
arr.join()
은 arr.reduce()
의 특별 버전입니다! 배열을 문자열로 합치는 데 특화되어 있습니다!
인수로 구분 문자를 넣을 수 있습니다! 그리고 arr.join()
은 null
이나 undefined
를 빈 문자열(""
)로 처리합니다!
참고로, arr.reduce()
말고 arr.reduceRight()
도 있습니다! 얘는 arr.reduce()
랑 똑같은데, 뒤에서부터 함수를 적용합니다!
const arr = ["a", "b", "c"];
arr.reduceRight((prev, cur) => prev + "-" + cur); // "c-b-a"
중복 제거하기 🚫
const arr = ["a", "i", "o", "i"];
Array.from(new Set(arr)); // ["a", "i", "o"]
배열에는 중복 제거하는 메서드가 없지만, Set 객체를 쓰면 가능합니다!
Set은 유일한 값만 저장하는 객체입니다! 중복을 허용하지 않습니다!
배열로 Set을 만들면 중복이 자동으로 제거됩니다! 그리고 그 Set으로 다시 배열을 만들면 중복 없는 배열 완성! ✨
문자열이나 숫자 같은 기본형 배열에서는 중복이 잘 제거되지만, 객체 배열에서는 내부 데이터 때문에 중복이 안 제거될 수도 있습니다! ⚠️
const arr = [
{ price: 100 },
{ price: 200 },
{ price: 100 },
];
Array.from(new Set(arr));
// [
// { price: 100 },
// { price: 200 },
// { price: 100 },
// ] (내부 값은 중복 제거 안 됨! 🙅♀️)
마무리 🎉
이번 여름방학에 꼭 알아야 할 자바스크립트 배열 조작법을 알아봤습니다! 😎
배열은 프로그래밍에서 정말 중요합니다! 배열을 잘 다룰 수 있으면 어떤 상황에서든 유용하게 쓸 수 있을 겁니다! 😉
오늘 배운 내용들을 직접 써보고, 깔끔하고 멋진 코드를 작성해보세요! 👍
'Javascript' 카테고리의 다른 글
React 컴파일러를 이용한 성능 최적화: 불필요한 렌더링은 이제 그만! (0) | 2024.08.04 |
---|---|
NextAuth와 Next.js: 미들웨어에서 세션 처리하는 다양한 방법 (0) | 2024.08.04 |
OAuth 배우기 전에 배우는 웹 인증의 세계 - Basic, Digest, 세션, 토큰 인증 비교 (0) | 2024.07.17 |
TypeScript에서 Array<T>와 T[]의 차이점 이해하기 (0) | 2024.07.17 |
JavaScript의 async, await에 대해 깊이 이해하기 (0) | 2024.07.01 |