TIL-31 자바스크립트 완벽 가이드, JS 100제

khundi·2022년 7월 4일
0
post-thumbnail

자바스크립트 완벽 가이드

7장 배열

  • 배열은 값의 순서가 있는 집합이다.
  • 각 값을 요소라고 부르며 각 요소에는 배열에서 차지하는 위치를 나타내는 숫자인 인덱스가 있다.
  • 배열 요소는 어떤 타입든 상관없다. 배열 하나에 여러 타입이 섞여 있어도 괜찮다.
  • 따라서 객체로 이루어진 배열도 가능하고 배열에 배열을 넣는 복잡한 데이터 구조를 만들 수 있다.
  • 자바스크립트의 배열은 '0으로 시작'하는 32비트 인덱스를 사용한다.
    첫 번째 요소의 인덱스는 0이고, 인덱스는 최대 4,294,967,294(232-2)까지 커질 수 있다. 따라서 배열의 값을 4,294,967,295개까지 담을 수 있다.
  • 자바스크립트는 성긴(sparse) 배열을 허용한다. 요소의 인덱스가 꼭 이어질 필요는 없고 그 사이에 갭이 있어도 된다.
  • 배열 인덱스는 프로퍼티 이름이 정수인 것과 별반 다르지 않다.
  • 자바스크립트 실행 환경은 일반적으로 배열을 최적화하므로 숫자로 인덱스된 배열 요소는 일반적인 객체 프로퍼티보다 상당히 빠르게 접근할 수 있다.
  • 배열은 Array.prototype에서 프로퍼티를 상속한다.

7.1 배열 생성

배열을 만드는 방법은 여러 가지가 있다.

  • 배열 리터럴
  • 이터러블 객체에 분해 연산자 ... 적용
  • Array() 생성자
  • Array.of()Array.from() 팩토리 메서드

배열 리터럴

배열을 만드는 가장 단순한 방법.

let empty = [];					// 요소가 없는 배열
let primes = [2, 3, 5, 7, 11];  // 숫자 요소가 다섯 개 있는 배열
let misc = [ 1.1, true, "a", ]; // 타입이 다른 요소가 세 개 있고 콤마로 끝난 배열

배열 리터럴에 상수를 쓸 필요는 없다. 아래처럼 임의의 표현식을 써도 된다.

let base = 1024;
let table = [base, base+1, base+2, base+3];

배열 리터럴에 콤마를 연속해서 썼는데 그 사이에 값이 없으면 성긴 배열이 만들어진다. 값을 생략한 위치를 검색하면 undefined를 반환한다.

let count = [1,,3]; // 인덱스 0과 2에는 요소가 있지만 인덱스 1에는 요소가 없다. 하지만 검색하면 undefined를 반환한다.
let undefs = [,,]   // 요소는 없지만 길이가 2이다. 마지막에 콤마를 허용하기 때문에 길이는 2이다.

분해 연산자

  • ES6 이후 분해 연산자 ...를 써서 배열 리터럴 안에 다른 배열 요소를 넣을 수 있다.
let a = [1, 2, 3];
let b = [0, ...a, 4]; // b == [0, 1, 2, 3, 4]

Array() 생성자

  • 인자 없이 호출
    let a = new Array();
    배열 리터럴 []과 같다.

  • 배열 길이를 나타내는 숫자 인자 하나로 호출
    let a = new Array(10);
    지정된 길이를 가진 배열을 생성함.

  • 배열 요소를 두 개 이상 쓰거나 숫자가 아닌 요소를 하나만 넘겨 호출
    let a = new Array(5, 4, 3, 2, 1, "testing");
    생성자의 인자가 새 배열의 요소가 된다.

Array.of()

  • Array() 생성자를 숫자 인자 하나만 넘겨 호출하면 길이가 그 숫자인 배열이 생성됨.
  • 숫자 인자가 하나 이상 있으면 이들 각각을 요소로 취급함.
  • 따라서 Array() 생성자로는 숫자 요소가 하나만 있는 배열은 생성할 수 없다.
  • 이 점을 해결하는 것이 Array.of() 함수이다.
Array.of()		// []; 인자가 없으므로 빈 배열
Array.of(10)	// [10]; 숫자 인자 하나만 있어도 된다.
Array.of(1,2,3) // [1, 2, 3]

Array.from()

  • ES6에서 도입한 팩토리 메서드이다.
  • 첫 번째 인자로 이터러블 객체나 배열 비슷한 객체를 받으며, 해당 객체의 요소로 새 배열을 만들어 반환한다.
  • Array.from(iterable)은 분해 연산자를 사용한 [...iterable]과 동등하다.
  • 선택 사항으로 두 번째 인자를 받는다. 두 번째 인자로 함수를 전달하면, 새 배열을 생성할 때 소스 객체의 각 요소를 이 함수에 전달하고 반환 값을 배열에 저장한다. 이 동작은 map() 메서드와 비슷하지만, 배열로 만든 다음 다시 map()을 써서 변환하기보다 처음부터 콜백 함수를 써서 변환하는 것이 효율적이다.

7.2 배열 요소 읽기와 쓰기

  • 배열 요소에 접근할 때는 [] 연산자를 사용하고, 대괄호 왼쪽에는 배열 참조, 대괄호 안에는 양의 정수인 표현식이 있어야 한다.
  • 인덱스에 값을 할당하면 그 배열의 length 프로퍼티가 자동으로 바뀐다.
  • 배열은 조금 특별한 타입의 객체이다. 객체는 존재하지 않는 프로퍼티를 검색해도 에러가 일어나지 않는다. undefined를 반환할 뿐이다. 배열도 마찬가지이다.

7.3 성긴 배열

  • 성긴 배열이란 인덱스가 연속적이지 않은 배열을 말한다.
  • 일반적으로 배열의 length 프로퍼티는 배열에 포함된 요소의 개수이지만 성긴 배열의 경우 length 프로퍼티의 값이 요소 개수보다 크다.
  • 성긴 배열은 Array() 생성자를 사용하거나 현재 배열 length 보다 큰 인덱스에 요소를 할당하면 만들어진다.
  • delete 연산자를 사용해 성긴 배열을 만들 수 있다.

7.4 배열 길이

  • 모든 배열에는 length 프로퍼티가 있다.
  • 이 프로퍼티는 일반적인 자바스크립트 객체와 배열을 구분하는 특징이다.
  • 배열의 length 프로퍼티를 현재 값보다 작은 양의 정수 n으로 지정할 때 인덱스가 n 이상인 배열 요소는 모두 삭제하는 동작이다.
a = [1,2,3,4,5];
a.length = 3;
console.log(a) // [1,2,3]
a.length = 0;
console.log(a) // []
a.length = 5;
console.log(a) // 길이는 5이지만 new Array(5)와 마찬가지로 요소는 없다.
  • length 프로퍼티를 현재 값보다 큰 값으로 설정할 수 있다. 하지만 새 요소가 추가되는 것은 아니고 배열 마지막에 성긴 영역이 생길 뿐이다.

7.5 배열 요소 추가와 삭제

배열 요소 추가 하는 방법

  • 새 인덱스에 값을 할당하는 방법
let a = [];
a[0] = "zero";
a[1] = "one";
  • push() 메서드를 사용하는 방법
    이 메서드는 배열 마지막에 값을 추가한다. a[a.length]에 값을 할당하는 것과 같다.
let a = [];
a.push("zero")
console.log(a) // ["zero"]
a.push("one", "two");
console.log(a) // ["zero", "one", "two"]
  • unshift() 메서드를 사용하는 방법
    이 메서드는 배열 앞에 값을 삽입하고 기존 요소를 뒤로 민다.

  • pop() 메서드는 push()의 반대로 배열의 마지막 요소를 제거하고 배열의 길이를 1만큼 줄인다.

  • shift() 메서드는 배열의 첫 번째 요소를 제거해 반환하고 길이를 1만큼 줄이고 나머지 요소를 모두 앞으로 당긴다.

  • delete 연산자로 배열 요소를 삭제할 수 있다. 배열 요소를 삭제하는 것은 그 요소에 undefined를 할당하는 것과 비슷하다.

  • 앞서 설명한것처럼 length 프로퍼티를 수정해서 배열의 맨 뒤에서부터 요소를 삭제하는 방법도 있다.

  • 배열 요소를 삽입, 삭제, 대체하는 범용 메서드인 splice()가 있다. 이 메서드는 length 프로퍼티를 변경하고, 필요에 따라 배열 요소를 앞뒤로 움직인다.

7.6 배열 순회

  • for 루프

  • for/of 루프
    ES6 이후 배열 요소나 이터러블 객체를 순회하는 가장 쉬운 방법.

  • forEach() 메서드
    배열을 순서대로 순회하며 배열 인덱스를 함수의 두 번째 인자로 전달함. for/of 루프와 달리 forEach()는 성긴 배열을 인식하고 존재하지 않는 요소에 대해서는 함수를 호출하지 않는다.

7.7 다차원 배열

자바스크립트에서 다차원 배열을 직접 지원하지는 않지만 배열의 배열을 만들어 대략적으로 흉내 낼 수 있다. 값을 접근할 때는 [] 연산자를 이중으로 쓰기만 하면 된다.

7.8 배열 메서드

배열 메서드란 Array 클래스에 정의된 메서드를 말한다.
메서드 중 일부는 원래 배열을 수정하고, 나머지는 배열을 수정하지 않는다. 일부 메서드는 원래 배열을 그대로 둔 채 새 배열을 반환한다. 나머지 메서드는 원래 배열을 수정하고 그 참조를 반환한다.

배열 이터레이터 메서드

  • 이 메서드는 배열 요소를 순서대로 함수에 전달하는 방식으로 동작하며 배열 요소를 순회, 변환, 필터, 체크, 축소(reduce) 할 수 있다.
  • 이 메서드들은 모두 첫 번째 인자로 함수를 받으며 각 배열 요소(또는 일부 요소)에 대해 그 함수를 한 번씩 호출한다. 성긴 배열이라면 존재하지 않는 요소는 호출하지 않는다.

forEach()
forEach() 메서드는 배열을 순회하며 각 요소에서 함수를 호출한다.
forEach()는 배열 요소의 값, 배열 요소의 인덱스, 배열 자체를 인자로 전달해 이 함수를 호출한다.
배열 요소의 값만 원하면 나머지는 무시해도 된다.
forEach()에서 모든 요소를 함수에 전달하기 전에 반복을 멈추는 방법이 없다.

map()
map() 메서드는 각 배열 요소를 함수에 전달해 호출하며, 그 함수가 반환한 값으로 이루어진 배열을 반환한다.
map() 메서드에 전달하는 함수는 값을 반환해야 한다. map()은 새 배열을 반환하며 기존 배열을 수정하지 않는다.
성긴 배열에 존재하지 않는 요소는 함수를 호출하지 않지만, 반환된 배열도 같은 위치에 갭이 있고 길이도 같다.

filter()
filter() 메서드는 기존 배열의 일부만 포함하는 부분 집합을 반환한다.
전달하는 함수를 기준으로 하며 이 함수는 true 또는 false를 반환한다.
반환 값이 true이거나 true로 변환될 수 있는 값이면, 해당 요소는 반환되는 배열에 추가된다.
성긴 배열에 존재하지 않는 값은 건너뛰며, 반환하는 배열은 항상 빽빽한 배열이다. 이를 이용해서 성긴 배열의 갭을 모두 제거할 수 있다.

find()와 findIndex()
find()와 findIndex() 메서드는 판별 함수에서 true 같은 값을 반환하는 요소를 찾아 순회한다는 점이 filter()와 같다.
하지만 이 메서드들은 기준하는 첫 번째 요소를 찾는 즉시 순회를 멈춘다.
만족하는 요소를 찾으면 find()는 그 요소를, findIndex()는 그 요소의 인덱스를 반환한다.
만족하는 요소를 찾지 못한다면 각각 undefined, -1을 반환한다.

every()와 some()
every()와 some() 메서드는 배열 요소에 판별 함수를 적용하고 결과에 따라 true 또는 false를 반환한다.
every() 메서드는 모든 요소에 대해 true를 반환할 때만 true를 반환한다.
some() 메서드는 모든 요소 중 하나라도 true라면 ture, 요소 전체가 false를 반환할 때만 false를 반환한다.
두 메서드는 자신이 어떤 값을 반환할지 확실해지는 순간 순회를 멈춘다.
every() 메서드는 false 반환을 받는 즉시 false를 반환하고,
some() 메서드는 true 반환을 받는 즉시 true를 반환한다.

reduce()와 reduceRight()
두 메서드는 제공하는 함수를 사용해 배열 요소를 값 하나로 만든다.
reduce()는 인자 두 개를 받는다. 첫 번째는 '축소' 동작을 행하는 함수이다.
이 함수가 하는 일은 어떤 방식으로든 값 두 개를 받아서 하나를 반환하는 것이다.
두 번째 인자는 선택 사항이며 함수에 전달할 초깃값이다.
reduceRight()는 reduce()와 마찬가지지만 오른쪽에서 왼쪽으로 진행한다는 점이 다르다.

flat()과 flatMap()을 사용한 배열 평탄화

  • ES2019에 도입되었다.
  • flat() 메서드는 기존 배열과 같은 요소로 이루어진 '평탄한'(중첩되지 않은) 새 배열을 반환한다.

예제

[1, [2, 3]].falt()    // [1, 2, 3]
[1, [2, [3]]].flat()  // [1, 2, [3]]
  • 인자 없이 호출하면 한 단계만 평탄화한다. 평탄화 레벨을 늘리려면 아래 예제와 같이 인자로 숫자를 전달하면 된다.
let a = [1, [2, [3, [4]]]];
a.flat(1) // [1, 2, [3, [4]]]
a.flat(2) // [1, 2, 3, [4]]
a.flat(3) // [1, 2, 3, 4]
a.flat(4) // [1, 2, 3, 4]
  • flatMap() 메서드는 map() 메서드와 똑같이 동작하지만, 반환하는 배열이 flat()에 전달한 것처럼 자동으로 평탄화된다는 점이 다르다.
    즉, a.flatMap(f)a.map(f).flat()과 동등하다. 하지만 더 효율적이다.
let phrases = ["hello world", "the definitive guide"]
let words = phrases.flatMap(phrase => phrase.split(" "))
words // [ 'hello', 'world', 'the', 'definitive', 'guide' ]

let words2 = phrases.map(phrase => phrase.split(" ")).flat()
words2 // [ 'hello', 'world', 'the', 'definitive', 'guide' ]

concat()으로 배열 병합

  • concat() 메서드는 기존 배열의 요소를 포함하고 그 뒤에 concat()의 인자를 포함하는 새 배열을 만들어 반환한다.

  • 인자에 배열이 들어 있으면 배열이 아니라 그 요소를 추가한다.

  • 하지만 concat()은 배열의 배열을 재귀적으로 평탄화하지는 않는다.

  • concat()은 기존 배열을 수정하지 않는다.

  • concat()은 원래 배열의 사본을 만들어 반환한다. 이는 비용이 많이 드는 작업이므로 a = a.concat(x) 같은 코드를 자주 쓰고 있다면 push()splice()를 대신 쓸 수 있을지 고민하는 것이 좋다.

스택과 큐 메서드

  • push()pop() 메서드는 배열을 스택처럼 다루는 메서드이다.
  • unshift()shift() 메서드는 push(), pop()과 거의 비슷하지만 배열의 마지막이 아니라 앞부분에서 이루어진다는 것이 다르다.
  • unshift()shift() 메서드는 앞부분을 삭제하고 추가하여 뒤에 있는 요소들을 한칸씩 앞뒤로 미는 작업이 수반되어야 하므로 비효율적이다.

하위 배열

slice()
slice() 메서드는 지정된 배열의 하위 배열을 반환한다.
두 개의 인자는 각각 반환될 슬라이스의 시작과 끝 위치를 나타낸다.
인자를 하나만 사용한다면 반환된 배열은 해당 위치부터 원래 배열의 마지막 요소까지 포함한다.
인자에 음수를 사용한다면 그 값에 배열 길이를 더한 값을 적용한다.

let a = [1,2,3,4,5]
a.slice(0,3)    // [1,2,3]
a.slice(3)      // [4,5]
a.slice(1, -1)  // [2,3,4]
a.slice(-3, -2) // [3]

splice()
splice()는 배열에 요소를 삽입하거나 제거하는 범용 메서드이다.
slice()나 concat()과는 달리 splice()는 원본 배열을 수정한다.
splice()는 요소를 삭제하거나 삽입할 수 있고 두 동작을 동시에 할 수 있다.
수정이 이루어진 다음 요소들을 앞뒤로 밀어 배열을 빽빽하게 유지한다.
splice()의 첫 번째 인자는 삽입이나 제거를 시작할 위치이다.
splice()의 두 번째 인자는 제거할 요소의 개수이다.
두 번째 인자를 생략하면 시작 지점부터 배열 마지막까지의 요소를 모두 제거한다.
splice()는 제거된 요소로 이루어진 배열을 반환한다. 제거한 것이 없다면 빈 배열을 반환한다.

let a = [1,2,3,4,5,6,7,8]
a.splice(4)   // [5,6,7,8]; a는 [1,2,3,4]
a.splice(1,2) // [2,3]; a는 [1,4]
a.splice(1,1) // [4]; a는 [1]

세 번째 인자부터는 개수 제한 없이 인자를 쓸 수 있으며 첫 번째 인자에서 지정한 위치에서부터 배열에 삽입된다.

let a = [1,2,3,4,5]
a.splice(2,0,"a","b") // []; a는 [1,2,"a","b",3,4,5]
a.splice(2,2,[1,2],3) // ["a","b"]; a는 [1,2,[1,2],3,3,4,5]

fill()
fill() 메서드는 배열의 요소 또는 슬라이스를 지정된 값으로 바꾼다. 이 메서드는 원본 배열을 수정한다.

let a = new Array(5);
a.fill(0)         // [0,0,0,0,0]; 배열을 0으로 채운다.
a.fill(9, 1)      // [0,9,9,9,9]; 인덱스 1에서 시작해 9로 채운다.
a.fill(8, 2, -1)  // [0,9,8,8,9]; 인덱스 2에서 시작해 3까지 8로 채운다.

첫 번째 인자는 배열 요소로 사용할 값이다.
두 번째 인자는 선택사항이며 시작 인덱스이다. 생략한다면 인덱스 0부터 시작한다.
세 번째 인자도 선택사항이며 끝을 정하는 인덱스이다. 생략한다면 마지막까지 진행한다.

copyWithin()
copyWithin()은 배열의 슬라이스를 복사해 새 위치에 넣습니다.
첫 번째 인자는 첫 번째 요소가 복사된 요소를 붙여넣기될 위치의 인덱스입니다.
두 번째 인자는 복사할 첫 번째 요소의 인덱스이다. 생략하면 0이다.
세 번째 인자는 복사할 마지막 인덱스를 지정한다. 생략하면 마지막까지 복사한다.

let a = [1,2,3,4,5]
a.copyWithin(1)       // [ 1, 1, 2, 3, 4 ]
a.copyWithin(2, 3, 5) // [ 1, 1, 3, 4, 4 ]
a.copyWithin(0, -2)   // [ 4, 4, 3, 4, 4 ]

copyWithin()은 고성능을 목표로 설계된 메서드며 형식화 배열에 특히 유용하다.

배열 검색과 정렬 메서드

indexOf()와 lastIndexOf()
indexOf()와 lastIndexOf()는 지정된 값을 배열에서 찾아 첫 번째 요소의 인덱스를 반환하며, 찾지 못하면 -1을 반환한다.
indexOf()는 앞에서부터 lastIndexOf()는 배열의 뒤에서부터 역순으로 검색한다.

let a = [0,1,2,1,0];
a.indexOf(1)      // 1  a[1]은 1이다.
a.lastIndexOf(1)  // 3  a[3]은 1이다.
a.indexOf(3)      // -1 값이 3인 요소가 없어서 -1을 반환함.

indexOf()와 lastIndexOf()는 요소를 찾을 때 === 연산자를 사용한다.
두 번째 인자를 선택사항으로 받는데 검색을 시작할 인덱스를 받는다.

includes()
ES2016에서 도입한 메서드이다. 인자 하나를 받고 배열에 그 값이 포함되어 있으면 true, 그렇지 않다면 false를 반환한다.
indexOf()와 차이가 있는데 indexOf()는 === 연산자를 사용해 찾아내는데 NaN은 자기 자신을 포함해 어떤 값과도 다르다고 판단한다. 하지만 includes()는 다른 알고리즘으로 NaN을 찾을 수 있다.

let a = [1, true, 3, NaN]
a.includes(true) // true
a.includes(2)    // false
a.includes(NaN)  // true
a.indexOf(NaN)   // -1; indexOf는 NaN을 찾지 못한다.

sort()
sort()는 배열 요소를 정렬한다.
인자 없이 호출하면 배열 요소를 알파벳순으로 정렬한다.
알파벳이 아닌 다른 순서로 배열을 정렬하고 싶다면 반드시 sort()에 비교 함수를 인자로 전달해야 한다.
이 함수는 정렬된 배열에서 어떤 인자가 앞에 있어야 하는지 결정한다.
첫 번째 인자가 두 번째 인자보다 앞에 있어야 한다면 비교 함수가 0보다 작은 숫자를 반환해야 한다.
반대로 첫 번째 인자가 뒤에 있어야 한다면 0보다 큰 숫자를 반환하면 된다.
그리고 두 값이 동등하다면(순서가 상관없다면) 함수가 0을 반환하면 된다.

reverse()
reverse() 메서드는 배열 요소의 순서를 거꾸로 바꾸어 반환한다.
이 메서드는 새 배열을 만들지 않고 원본 배열의 요소 순서를 뒤집는다.

배열을 문자열로 변환

join()
join() 메서드는 배열 요소 전체를 문자열로 변환한 다음, 이들을 병합한 결과를 반환한다.
다른 자바스크립트 객체와 마찬가지로 배열에도 toString() 메서드가 있어 toString()을 쓰면 join() 메서드를 인자 없이 호출한 것과 같다.

7.10 배열인 문자열

charAt()

자바스크립트의 문자열은 UTF-16 유니코드 문자로 구성된 읽기 전용 배열처럼 동작한다.

let s = "test";
s.charAt(0) // "t"
s[1]        // "e"

JS 100제

- 문제70

새로운 인사이트

  • 행렬의 곱이 성립하려면 a열의 개수와 b행의 개수가 일치해야 곱할 수 있다.
  • c = a[i][k] * b[k][j]
    i j k 가 순서대로 for문이 종속관계에 있을 때 성립한다.
  • 선형대수 라이브러리를 이용하면 쉽게 코드를 작성할 수 있다.

<출처-JS 100제 문제70>
https://www.notion.so/70-289a40f58bfa4c76b4aa749c2c42ac54

profile
안녕하세요. 웹 프론트엔드 개발자 전성훈입니다.

0개의 댓글