Study JavaScript 0513 - 1hr 배열과 메소드

변승훈·2022년 5월 13일
0

Study JavaScript

목록 보기
18/43

배열과 메소드

요소 추가·제거 메서드

배열의 맨 앞이나 끝에 요소(item)를 추가하거나 제거하는 메소드는 12일에 이미 학습한 바 있다.

  • arr.push(...items): 맨 끝에 요소 추가
  • arr.pop(): 맨 끝 요소 제거
  • arr.shift(): 맨 앞 요소 제거
  • arr.unshift(...items): 맨 앞에 요소 추가

이 외의 메소드를 알아보자.

splice

배열에서 요소를 하나만 지우고 싶다면 delete를 사용해 볼 수 있다.

    let arr = ["I", "go", "home"];

    delete arr[1]; // "go"를 삭제

    console.log( arr[1] ); // undefined

    // delete를 써서 요소를 지우고 난 후 배열 --> arr = ["I",  , "home"];
    console.log( arr.length ); // 3

하지만 원하는 요소를 지웠음에도 배열의 요소는 여전히 갯수가 같다.

이를 해결하기 위해 splice()메소드를 사용한다.
arr.splice(index[, deleteCount, elem1, ..., elemN])
첫 번째 매개변수(index)는 조작을 가할 첫 번째 요소를 가리키는 index이다.
두 번째 매개변수(deleteCount)는 제거하고자 하는 요소의 개수이다.
마지막(elem1, ..., elemN)은 배열에 추가할 요소를 나타낸다.

  1. 요소 삭제
    let arr = ["I", "study", "JavaScript"];

    arr.splice(1, 1); // 인덱스 1부터 요소 한 개를 제거

    console.log( arr ); // ["I", "JavaScript"]
  1. 요소 삭제와 교체 동시 진행
    let arr = ["I", "study", "JavaScript", "right", "now"];

    // 처음(0)부터 세 개(3)의 요소를 지우고, 이 자리를 다른 요소로 대체
    arr.splice(0, 3, "Let's", "dance");

    console.log( arr ) // now ["Let's", "dance", "right", "now"]
  1. 삭제된 요소로 구성된 배열 반환
    let arr = ["I", "study", "JavaScript", "right", "now"];

    // 처음 두 개의 요소를 삭제함
    let removed = arr.splice(0, 2);

    console.log( removed ); // "I", "study" <-- 삭제된 요소로 구성된 배열
  1. deleteCount가 0일 때, 새로운 요소 추가
    let arr = ["I", "study", "JavaScript"];

    // 인덱스 2부터 0개의 요소를 삭제
    // 그 후, "complex"와 "language"를 추가
    arr.splice(2, 0, "complex", "language");

    console.log( arr ); // "I", "study", "complex", "language", "JavaScript"
  1. 음수 index 사용: 배열의 끝에서 부터 센 요소의 위치
    let arr = [1, 2, 5];

    // 인덱스 -1부터 (배열 끝에서부터 첫 번째 요소) 0개의 요소를 삭제하고 3과 4를 추가
    arr.splice(-1, 0, 3, 4);

    console.log( arr ); // 1,2,3,4,5

slice

arr.slice([start], [end])
이 메소드는 "start" 인덱스부터 ("end"를 제외한) "end"인덱스까지의 요소를 복사한 새로운 배열을 반환한다.
start와 end는 둘 다 음수일 수 있는데 이때는 배열 끝에서부터의 요소 개수를 의미한다.

arr.slice는 문자열 메서드인 str.slice와 유사하게 동작하는데 arr.slice는 서브 문자열(substring) 대신 서브 배열(subarray)을 반환한다는 점이 다르다.

★slice는 인수를 하나도 넘기지 않고 호출하여 arr의 복사본을 만들 수 있다.
기존의 배열을 건드리지 않고 배열을 조작해 새로운 배열을 만들고자 할 때 자주 사용된댜.

    let arr = ["t", "e", "s", "t"];

    console.log( arr.slice(1, 3) ); // e,s (인덱스가 1인 요소부터 인덱스가 3인 요소까지를 복사(인덱스가 3인 요소는 제외))

    console.log( arr.slice(-2) ); // s,t (인덱스가 -2인 요소부터 제일 끝 요소까지를 복사)

concat

arr.concat(arg1, arg2...)
기존 배열의 요소를 사용해 새로운 배열을 만들거나 기존 배열에 요소를 추가하고자 할 때 사용할 수 있다.
인수로는 배열이나 값이 올 수 있고 개수엔 제한이 없다.
메소드 호출 시 새로운 배열이 반환 된다.
인수가 배열이면 배열의 모든 요소가 복사되며 그렇지 않으면 인수가 그대로 복사된다.

  1. 인수 그대로를 복사하여 반환
    let arr = [1, 2];

    // arr의 요소 모두와 [3,4]의 요소 모두를 한데 모은 새로운 배열이 만들어진다.
    console.log( arr.concat([3, 4]) ); // 1,2,3,4

    // arr의 요소 모두와 [3,4]의 요소 모두, [5,6]의 요소 모두를 모은 새로운 배열이 만들어진다.
    console.log( arr.concat([3, 4], [5, 6]) ); // 1,2,3,4,5,6

    // arr의 요소 모두와 [3,4]의 요소 모두, 5와 6을 한데 모은 새로운 배열이 만들어진다.
    console.log( arr.concat([3, 4], 5, 6) ); // 1,2,3,4,5,6
  1. 배열의 모든 요소가 복사
    let arr = [1, 2];

    let arrayLike = {
      0: "something",
      length: 1
    };

    console.log( arr.concat(arrayLike) ); // 1,2,[object Object]
  1. 인자로 받은 유사 배열 객체에 특수한 프로퍼티 Symbol.isConcatSpreadable이 있으면 concat은 이 객체를 배열처럼 취급한다. 따라서 객체 전체가 아닌 객체 프로퍼티의 값이 더해진다.
    let arr = [1, 2];

    let arrayLike = {
      0: "something",
      1: "else",
      [Symbol.isConcatSpreadable]: true,
      length: 2
    };

    console.log( arr.concat(arrayLike) ); // 1,2,something,else

forEach

주어진 함수를 배열 요소 각각에 대해 실행할 수 있게 해준다.
인수로 넘겨준 함수의 반환값은 무시된다.

    arr.forEach(function(item, index, array) {
      // 요소에 무언가를 할 수 있다.
    });

    ["Bilbo", "Gandalf", "Nazgul"].forEach((item, index, array) => {
      console.log(`${item} is at index ${index} in ${array}`);
    });

배열 탐색

indexOf, lastIndexOf, includes

  • arr.indexOf(item, from): 인덱스 from부터 시작해 item(요소)을 찾는다. 요소를 발견하면 해당 요소의 인덱스를 반환하고, 발견하지 못했으면 -1을 반환한다.
  • arr.lastIndexOf(item, from): 위 메서드와 동일한 기능을 하는데, 검색을 끝에서부터 시작한다는 점만 다르다.
  • arr.includes(item, from): 인덱스 from부터 시작해 item이 있는지를 검색하는데, 해당하는 요소를 발견하면 true를 반환한다.
    이 메소드들은 "==="을 사용한다는 점을 유의해야 하며 includes는 NaN도 제대로 처리를 한다
    let arr = [1, 0, false];

    console.log( arr.indexOf(0) ); // 1
    console.log( arr.indexOf(false) ); // 2
    console.log( arr.indexOf(null) ); // -1

    console.log( arr.includes(1) ); // true

find와 findIndex

  1. arr.find(fn): 객체로 이루어진 배열에서 특정 조건에 부합하는 객체를 배열 내에서 찾을 때 사용한다.
    let result = arr.find(function(item, index, array) {
      // true가 반환되면 반복이 멈추고 해당 요소를 반환
      // 조건에 해당하는 요소가 없으면 undefined를 반환
    });

    let users = [
      {id: 1, name: "John"},
      {id: 2, name: "Pete"},
      {id: 3, name: "Mary"}
    ];

    let user = users.find(item => item.id == 1);

    console.log(user.name); // John

요소 전체를 대상으로 함수가 순차적으로 호출된다.

  • item: 함수를 호출할 요소
  • index: 요소의 인덱스
  • array: 배열 자기 자신
    함수가 참을 반환하면 탐색을 중단하고 해당 요소가 반환되고 찾지 못하면 undefined를 반환한다.
  1. arr.findIndex: find와 동일한 일을 하나, 조건에 맞는 요소를 반환하는 대신 해당 요소의 인덱스를 반환한다는 점이 다르며, 조건에 맞는 요소가 없으면 -1이 반환된다.

filter

find 메소드는 함수의 반환 값을 true로 만드는 단 하나의 요소를 찾지만, 조건을 충족하는 요소가 여러개면 arr.filter(fn)를 사용한다.
filterfind와 문법이 유사하지만, 조건에 맞는 요소 전체를 담은 배열을 반환한다는 점에서 차이가 있다.

    let results = arr.filter(function(item, index, array) {
      // 조건을 충족하는 요소는 results에 순차적으로 더해진다
      // 조건을 충족하는 요소가 하나도 없으면 빈 배열이 반환된다
    });
    
    let users = [
      {id: 1, name: "John"},
      {id: 2, name: "Pete"},
      {id: 3, name: "Mary"}
    ];

    // 앞쪽 사용자 두 명을 반환한다.
    let someUsers = users.filter(item => item.id < 3);

    console.log(someUsers.length); // 2

배열을 변형하는 메소드

map

map은 배열 요소 전체를 대상으로 함수를 호출하고, 함수 호출 결과를 배열로 반환해준다.

    let result = arr.map(function(item, index, array) {
      // 요소 대신 새로운 값을 반환
    });

    let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length);
    console.log(lengths); // 5,7,6

sort(fn)

배열의 요소를 정렬해주며 배열 자체가 변경된다.

reverse

배열의 요소를 역순으로 정렬시켜주는 메소드이며, 반환 값은 재 정렬된 배열이다.

    let arr = [1, 2, 3, 4, 5];
    arr.reverse();

    console.log( arr ); // 5,4,3,2,1

split과 join

  • split(구분자, 숫자): 구분자를 기준으로 문자열을 쪼개주고 숫자로 배열의 길이를 제한해주어 길이를 넘어서는 요소를 무시할 수 있다.
    숫자는 실무에서 자주 사용하는 기능은 아니다.
    let str = "test";

    console.log( str.split('') ); // t,e,s,t
    
    let arr = 'Bilbo, Gandalf, Nazgul, Saruman'.split(', ', 2);

    console.log(arr); // Bilbo, Gandalf
  • join(): split과 반대 역할을 하는 메소드이며 배열의 모든 요소를 합친 후 하나의 문자열로 만들어 준다.
    let arr = ['Bilbo', 'Gandalf', 'Nazgul'];

    let str = arr.join(';'); // 배열 요소 모두를 ;를 사용해 하나의 문자열로 합친다.

    console.log( str ); // Bilbo;Gandalf;Nazgul

reduce와 reduceRight

forEach, for, for..of를 사용하면 배열 내 요소를 대상으로 반복 작업을 할 수 있다.
arr.reduce와 arr.reduceRight도 이런 메소드들과 유사한 작업을 해주는데 사용법이 복잡하다.
배열의 기반으로 값 하나를 도출할 때 사용된다.

    let value = arr.reduce(function(accumulator, item, index, array) {
      // ...
    }, [initial]);

함수의 인수는 다음과 같다.

  • accumulator: 이전 함수 호출의 결과. initial은 함수 최초 호출 시 사용되는 초깃값을 나타냄(옵션)
  • item: 현재 배열 요소
  • index: 요소의 위치
  • array: 배열

첫 번째 인수는 앞에서 호출헸던 함수들의 결과가 누적되는 누산기라 생각하면 된다.

    let arr = [1, 2, 3, 4, 5];

    // reduce에서 초깃값을 제거함(0이 없음)
    let result = arr.reduce((sum, current) => sum + current);

    console.log( result ); // 15

Array.isArray로 배열 여부 알아내기

배열은 자료구조이기 때문에 typeof로 일반 객체와 배열을 구분할 수 없다. 배열인지 아닌지 감별해내는 메소드이다.

    console.log(typeof {}); // object
    console.log(typeof []); // object
    console.log(Array.isArray({})); // false
    console.log(Array.isArray([])); // true

배열 메소드와 'thisArg'

함수를 호출하는 대부분의 배열 메서드(find, filter, map 등. sort는 제외)는 thisArg라는 매개변수를 옵션으로 받을 수 있다.

    let army = {
  minAge: 18,
  maxAge: 27,
  canJoin(user) {
    return user.age >= this.minAge && user.age < this.maxAge;
    } 
  };

  let users = [
    {age: 16},
    {age: 20},
    {age: 23},
    {age: 30}
  ];

  // army.canJoin 호출 시 참을 반환해주는 user를 찾음
  let soldiers = users.filter(army.canJoin, army);

  console.log(soldiers.length); // 2
  console.log(soldiers[0].age); // 20
  console.log(soldiers[1].age); // 23

thisArgsarmy를 지정하지 않고 단순히 users.filter(army.canJoin)를 사용했다면 army.canJoin은 단독 함수처럼 취급되고, 함수 본문 내 thisundefined가 되어 에러가 발생했을 것이다.

users.filter(user => army.canJoin(user))를 사용하면 users.filter(army.canJoin, army)를 대체할 수 있긴 한데 thisArg를 사용하는 방식이 좀 더 이해하기 쉬우므로 더 자주 사용된다.

profile
잘 할 수 있는 개발자가 되기 위하여

0개의 댓글