ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 한방에 자바스크립트 배열 마스터하기(뽀개기)!
    Javascript 2024. 8. 3. 11:53

     

    한 여름입니다! 🏖️

     

    드디어 코딩 실력 좀 올려볼까요? 🤔

     

    이번엔 자바스크립트에서 절대 빼놓을 수 없는 배열 뽀개기에 도전해봅시다! 💪

     

    자주 쓰는 배열 조작법부터 최신 기술까지, 알차게 준비했으니까 🤓 이번 방학에 배열 마스터하고 코딩 고수로 레벨업! 🚀

     

    이 글을 읽으면 뭐가 좋냐고요? 😉

     

    맨날 쓰던 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문의 continuearr.forEach()에서 return으로 대체 가능!

     

    하지만 breakarr.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 },
    // ] (내부 값은 중복 제거 안 됨! 🙅‍♀️)

    마무리 🎉

    이번 여름방학에 꼭 알아야 할 자바스크립트 배열 조작법을 알아봤습니다! 😎

     

    배열은 프로그래밍에서 정말 중요합니다! 배열을 잘 다룰 수 있으면 어떤 상황에서든 유용하게 쓸 수 있을 겁니다! 😉

     

    오늘 배운 내용들을 직접 써보고, 깔끔하고 멋진 코드를 작성해보세요! 👍

     

Designed by Tistory.