JavaScript | 배열

SURI·2021년 11월 19일

1. 개념


1.1 배열의 특징

  • 반복문이 수 많은 데이터를 한 번에 처리하는 방법이라면, 배열과 객체는 대량의 정보를 보관하고 관리하는 방법이다.
  • 배열과 객체는 참조 타입(reference type) 데이터다. 원시 타입(숫자, 문자열, boolean, undefined, etc) 데이터와는 다른 성격을 가진다.

1.2 배열의 용어

  • index : 배열은 순서가 있는 값이고 순서는 인덱스라고 부른다. 순서는 0부터 센다.
  • element : 배열의 값을 요소라고 하고 쉼표로 구분한다.
  • .length : 배열의 길이를 알려면 length를 사용하면 된다.
    • 온점을 이용해 변수가 가지고 있는 속성에 접근할 수 있다.
    • 온점을 이용해 관련된 명령도 실행할 수 있다.

1.3 배열의 요소 C/R/U/D

let arr = ['suri', 'masuri'];

arr[0]; // 배열의 요소 조회하기
arr[1] = 'leesuri'; // 배열의 요소 값 변경하기
arr[2] = 'very'; // 배열에 요소 추가하기
arr[3]; // undefined

arr.push('lovely'); // 배열 끝에 값을 추가한다.
arr.pop(); // 마지막 값을 삭제한다.
arr.unshift('creative', 'smart'); // 앞에 값을 추가한다.
arr.shift(); // 앞에 값을 삭제한다.

console.table(arr); // 배열을 표로 직관적으로 확인 가능
  • 배열의 값은 bracket notation과 인덱스를 이용해 접근한다.

  • 배열의 값 변경은 변수에 할당하는 것과 동일하다.

  • 배열의 길이보다 긴 인덱스를 통해 접근하면 에러가 뜨지 않고 undefined를 리턴한다.

  • 자바스크립트에서는(?) 배열의 기존 인덱스보다 더 큰 인덱스로 추가를 해도 에러를 띄우지 않고 값이 할당이 된다.

    unshiftshiftpushpop
    첫 번째 엘리먼트첫 번째 엘리먼트마지막 엘리먼트마지막 엘리먼트
    추가제거추가제거
    새 배열의 길이 리턴제거한 요소 리턴새 배열의 길이 리턴제거한 요소 리턴

이중배열

배열의 요소가 배열인 경우가 있다. 즉, 배열 안에 배열이 들어 있는 이중 배열에 대해 알아보자.

let arr = [[2, 3, 4], 5, 6];
arr[0]; // [2, 3, 4]
Array.isArray(arr[0]); // true

1.4 배열의 메소드

문자열과 배열의 타입 변환

arr.join() vs str.split()

// 배열 -> 문자열
let arr = ['안녕', '정말', '반갑구나!'];
console.log(arr.join(" ")); // '안녕 정말 반갑구나!'

// 문자열 -> 배열
let words = "정말 좋은 날이야!";
console.log(words.split(" ")); // ['정말', '좋은', '날이야!']
console.log(words.split("")); // ['정', '말', ' ', '좋', '은', ' ', '날', '이', '야', '!']
  • split()에서 나누어줄 기준이 없으면 문자열을 배열에 그대로 넣어 반환한다.

배열을 복사/분리

arr.slice()

  • begin부터 end전까지(미포함) 복사본을 새로운 배열 객체로 반환한다.
  • end가 생략되면 배열의 끝까지 복사한다. 인자가 모두 생략되면 배열을 그대로 복사한다.
  • 원본 배열은 수정되지 않는다.

arr.splice()

  • arr.splice(제거 시작 인덱스, 제거 엘리먼트 수, 추가할 엘리먼트 1/2/3..)
  • 배열의 기존 요소를 삭제 혹은 교체하거나 새 요소를 추가할 수 있다.
    • 제거와 추가를 동시에 하고 싶을 때 사용하는 메서드다.
    • 중간에 값을 넣고 싶을 때 많이 사용한다.
  • 원본 배열 자체를 수정한다.
  • 반환값은 제거한 요소를 담은 배열을 반환한다.
    • 제거된 값이 없으면 빈 배열을 반환하는구나. (k23)
  • 문자열에 splice()를 사용하려고 했더니 uncaught typeerror를 띄운다.

* reference site : https://im-developer.tistory.com/103

배열인지 아닌지 확인

Array.isArray()

let words = ['안녕', '반가워', '이름은?'];

console.log(typeof words); // object
console.log(Array.isArray(words)); // true
  • typeof를 이용해 배열을 검사하면 object를 리턴한다. (배열은 객체를 활용해서 만든 거다) null도 object로 리턴한다.
  • 자바스크립트에서는 array와 null이라는 타입이 존재하지 않는건가 싶다.

배열에 특정 값 확인

indexOf() vs includes()

let words = ['life', 'is', 'Fun'];
words.indexOf('fun'); // -1 (없는 값이면 -1을 보여준다)
words.indexOf('life'); // 0 (인덱스를 보여준다)

console.log(words.indexOf('fun') !== -1) // 배열에 특정 엘리먼트가 있는지 검사하는 방법

// 유틸리티 함수 만들어보기
function hasElement(arr, el) {
    return arr.indexOf(el) !== -1
}

console.log(hasElement(words, 'Fun')); // true

// 내장 함수 includes) 
words.includes('Fun') // true
words.includes('hate') // false
  • 결론적으로 indexOf가 범용성이 더 넓다. 또한 includes는 IE 호환성 문제가 있다.

배열 합치기

array.concat(newArray)

let firstArray = [1, 2];
let secondArray = [1, '하이'];
let thirdArray = [];

firstArray.concat(secondArray, thirdArray); // [1, 2, 1, '하이']
  • 새로운 배열이 생성되고 원본 배열을 수정하지 않는다.

mutable vs immutable

  • 문자열과 배열을 다루는 메소드를 활용할 때, 그 메소드가 원본 문자열과 배열을 수정하는가 여부를 기억해야 한다.
    • 해당 데이터의 주소를 찾아서 값을 변경
    • 해당 데이터 주소와 별개의 새로운 주소에 값이 할당
  • 그 메소드가 무엇을 리턴하는지에 대해서도 생각한다.

* reference site : https://doesitmutate.xyz/

1.5 배열과 반복문

let arr = [1, '가', 2, '나', 3, '다', 4, '라', 5, '마', 6];

for (let i = 0; i < arr.length; i++) {
    if (i % 2 === 0) {
        arr[i] = `숫자 : ${arr[i]}`;
    } else {
        arr[i] = `문자 : ${arr[i]}`;
    }
}

console.table(arr);
  • 배열의 요소를 반복적으로 불러온다.
  • 배열의 각 요소에 대하여, 반복하는 코드를 명령한다.
  • 반복문에 조건문을 넣어 특정 조건에서만 반복문을 실행시킨다.

배열과 이중 반복문

2. 에러로그


빈 배열을 확인하는 명료한 방법

  • arr.length === 0
  • arr[0] === undefined 라고 조건을 걸어줬는데, 테스트 케이스는 통과했지만 빈 배열이 아니어도 undefined를 요소로 가지고 있는 배열이 분명히 존재한다.
  • [] === [] // false 겉보기엔 같아도, 저장되어 있는 위치 주소 값이 다르기 때문에 false가 뜬다.

빈 문자열의 길이도 0이다.

  • ''.length === 0 // true

else를 떠올리고 if를 떠올렸다면, else if의 경우다. (k4)

  • 경우의 수가 총 3가지 였다. 빈 배열인 경우, index가 배열의 길이보다 큰 경우, 나머지 경우. 여기에서 else if를 써줬으면 되었다. else 안에 if를 쓰려고 했었다.

초기화를 잘 해놓으면 조건을 줄일 수 있다. (k5)

for of 문법을 활용하면 좀 더 간단하게 코드를 짤 수 있다. (k8)

for (let el of arr) {
    if (el > max) {
      max = el;
    }
  }

for (let i = 0; i < arr.length; i++) {
    if (maxElement < arr[i]) {
      maxElement = arr[i];
    }
  }
  • 위와 아래는 정확히 같은 의미다.
  • 꼭 인덱스를 1부터 시작하지 않아도 상관은 없다.
  • 지금 이 반복문이 돌아가는 걸 컨베이어 벨트 기계라고 생각하니까 좀 더 쉽게 다가 온다.

원본 배열을 바꾸는 메소드를 반복문에 사용하니 인덱스가 달라진다. (k10)

  • 반복문마다 해당하는 인덱스를 지우는 방식을 해보려고 했으나, 원본 배열이 바뀌면서 인덱스가 달라지게 된다.

문제를 풀 때, 주의사항과 입출력 예시를 꼼꼼하게 읽지 않아서 놓치는 경우의 수를 만들다.

배열을 합치는 경우

  • return arr1.concat(arr2);
  • return [...arr1, ...arr2]
    • spread 문법을 활용하면 배열이나 객체에 요소나 속성을 조립할 때 편리하다.
    • concat을 활용해 배열이나 문자열을 이어붙일 수 있다는 거! 배열에 객체도 이어붙일 수 있다.

arr.shift()를 그대로 리턴했더니, 통과하지 못한다. (k16)

  • 위 메서드는 원본을 수정하는 메서드이고, 제거된 값을 반환한다. 우리가 원하는 것은 값이 제거된 원본 배열이므로 return arr;을 해야 한다.

배열의 마지막 인덱스는 arr.length - 1이다.

  • slice 메서드를 사용할 때, end (미포함) 인자를 넣을 때 헷갈린다.

새로운 배열을 만들어 기존 배열을 할당했는데 원본 배열이 바뀌었다. (k21)

// const newArr = arr;
const newArr = arr.slice();
newArr.push(el);
return newArr;
  • 조건에서 원본 배열을 수정하지 않아야 된다고 했다. 하지만 주석 처리된 첫 번째 줄로 하면 원본 배열이 수정된다고 나온다.
    • 왜? 나는 배열의 값을 할당해준건데? 사실은 아니었다.
    • 여기에서 arr은 배열의 그 값 자체가 아니라 배열을 가리키는 참조변수(포인터)였다. newArr에 그 포인터를 그대로 할당한 것이니, 결국은 두 변수 모두 같은 배열(원본)을 가리키게 된다.
    • 좀 더 깊숙하게 들어가면 deep copy에 대해 알아볼 수 있다.

변수명과 slice 인덱스 인자에 대한 아이디어 (k24)

  • 휴대폰 자릿수에 대한 변수명을 지정할 때 나는 sec1/2/3으로 했지만 레퍼런스는 head/body/tail 라고 해준 것
  • arr.slice(len - 8, len - 4) 이게 어떤 인덱스를 나타내는지 나는 바로 떠올릴 수 있을까?
    • slice의 인자로 음수를 받을 수도 있다. 위에서 len을 생략하고 뒤에서부터 셀 수도 있다.
  • unshift에 인자를 여러개 넣을 수 있다는 사실? push도 마찬가지였다!

반복문/조건문 활용해 인덱스에 따라 다른 조건을 걸어줄 수 있다. (k25)

  • 인덱스 0과 1은 패턴이 없고 2부터 패턴이 있는 경우였다. 나는 초기값으로 그 경우를 지정하고 시작을 했다. 그러다가 놓치는 인덱스가 있었다.
  • 반복문 안에 조건문에 따라서 모든 경우의 수를 꼼꼼하게 챙겨주는 편이 더 나은 것 같다. 인덱스가 0일 때, 1일 때, 다른 경우 일 때. 결국 순차적으로 이 조건문을 다 실행하고 넘어가게 된다.
  • 근데 정말 이 코드가 더 나을까? 차라리 패턴이 없는 걸 반복문에서 꺼내서 챙겨주는 게 더 나은 걸 아닐까?

초기값을 잘못 할당하면 도로묵이다.

  • 음수를 요소로 가진 배열에서 가장 큰 값을 찾고 싶다. 그런데 만약에 let maxElement = 0;으로 시작했다면?
  • 이런 경우를 방지하기 위해서는 가능하면 모든 경우의 수를 반영하려고 노력해야 한다.

반복문의 횟수와 내가 만들고 싶은 배열의 인덱스가 다르다.

let arr1 = [1, 2, 3, 4, 5];
let arr2 = ['가', '나', '다', '라', '마'];

let newArray = [];
let index1 = 0;
let index2 = 0;
for (let i = 0; i < arr1.length + arr2.length; i++) {
    if (i % 2 === 0) {
        newArray.push(arr2[index1]);
        index1++;
    } else {
        newArray.push(arr1[index2]);
        index2++;
    }
    
}

console.table(newArray);
  • 반복문은 n번 반복되는데 조건문에 따라서 인덱스가 1/n번으로 나뉘는 경우 어떻게 처리해야 할까 잠깐 고민이 되었다. 인덱스 변수를 여러 개 설정해줘서 나눠주는 방법으로 처리했다.

syntax error

  • typeof를 메소드처럼 사용하려고 했다.
  • 빈 문자열을 나타내고 싶은데 그 안에 공백을 넣지 않아야 한다 str = '';

3. 질문


  • result1/2/3/.. 이런 변수명을 가진 변수들이 있다고 할 때, 이 값들을 반복문을 돌려서 모두 출력하는 방법
  • let을 쓸 때와 const를 쓸 때의 차이점 (k21)
  • !array.length 이건 무슨 표현?
  • break와 continue 사용
    • 현재 반복문을 끝낸다는 뜻이고, continue는 해당 경우만 패스?
profile
Every step to become a better version of me 🚶‍♂️ 블로그 이사중

0개의 댓글