티스토리 뷰

728x90
SMALL

배열

배열과 객체를 구분하는 가장 명확한 차이는 값의 순서와 length 프로퍼티다.

자바스크립트 배열은 배열이 아니다.

보통 자료구조에서 말하는 배열은 동일한 크기의 메모리 공간이 빈틈없이 연속적으로 나열된 자료구조를 말한다. 즉 배열의 요소는 하나의 데이터 타입으로 통일되어 있으며 서로 연속적으로 인접해 있다. 이러한 배열을 밀집 배열이라 한다.

but 자바스크립트에서는 배열의 요소를 위한 각각의 메모리 공간은 동일한 크기를 갖지 않아도 되며, 연속적으로 이어져 있지 않을 수도 있다. 배열의 요소가 연속적으로 이어져 있지 않는 배열을 희소 배열이라 한다.

자바스크립트 배열은 일반적인 배열의 동작을 흉내 낸 특수한 객체다.

예제를 보면 인덱스를 나타내는 문자열을 프로퍼티 키로 가지며, length 프로퍼티를 갖는 특수한 객체다.

console.log(Object.getOwnPropertyDescriptors([1, 2, 3]));
/*
{
  '0': {value: 1, writable: true, enumerable: true, configurable: true}
  '1': {value: 2, writable: true, enumerable: true, configurable: true}
  '2': {value: 3, writable: true, enumerable: true, configurable: true}
  length: {value: 3, writable: true, enumerable: false, configurable: false}
}
*/

 

장단점

일반적인 배열은 인덱스로 요소에 빠르게 접근, but 특정 요소를 검색하거나 요소를 삽입, 삭제하는 경우 효율 안좋음

자바스크립트 배열은 해시 테이블로 구현된 객체이므로 인덱스로 요소에 접근하는 경우 일반적인 배열보다 성능적인 면에서 느릴수밖에 없는 구조적인 단점. but 특정 요소를 검색하거나 요소를 삽입 또는 삭제하는 경우에는 일반적인 배열보다 빠른 성능을 기대(인덱스로 접근할 때 느리다는 단점을 해결하기 위해 대부분 엔진들은 최적화를 한다.)

 

length 프로퍼티를 지정할 수도 있다. 현재 length보다 작은 값을 할당하면 배열의 길이가 줄어들고 큰 값을 할당하면 empty값이 생긴다. 이때 빈 공간에 메모리 공간을 확보하지 않는다.(희소배열)

되도록이면 희소배열을 생성하지 말자. 배열에는 같은 타입의 요소를 연속적으로 위치시키는 것이 최선이다.

 

배열 생성

1. 배열 리터럴 arr=[]

2. 생성자 함수

new Array(10) => length10인 희소배열

new Array() => []

new Array(1,2,3) =>[1,2,3]  // 전달된 인수가 2개 이상이거나 수자가 아닌 경우 인수를 요소로 갖는 배열을 생성

3. Array.of

전달된 인수를 요소로 갖는 배열을 생성한다. 생성자 함수와 다르게 전달된 인수가 1개이고 숫자이더라도 배열 생성

Array.of(1) => [1]

4. Array.from

유사 배열 객체 또는 이터러블 객체를 인수로 전달받아 배열로 변환하여 반환한다.

// 유사 배열 객체를 변환하여 배열을 생성한다.
Array.from({ length: 2, 0: 'a', 1: 'b' }); // -> ['a', 'b']

// 이터러블을 변환하여 배열을 생성한다. 문자열은 이터러블이다.
Array.from('Hello'); // -> ['H', 'e', 'l', 'l', 'o']

// Array.from에 length만 존재하는 유사 배열 객체를 전달하면 undefined를 요소로 채운다.
Array.from({ length: 3 }); // -> [undefined, undefined, undefined]

// Array.from은 두 번째 인수로 전달한 콜백 함수의 반환값으로 구성된 배열을 반환한다.
Array.from({ length: 3 }, (_, i) => i); // -> [0, 1, 2]

 

인덱스는 요소의 위치를 나타내므로 반드시 0이상의 정수를 사용해야 한다. 만약 정수 이외의 값을 인덱스처럼 사용하면 요소가 생성되는 것이 아니라 프로퍼티가 생성된다. 프로퍼티는 length값에 영향 x

const arr = [];

// 배열 요소의 추가
arr[0] = 1;
arr['1'] = 2;

// 프로퍼티 추가
arr['foo'] = 3;
arr.bar = 4;
arr[1.1] = 5;
arr[-1] = 6;

console.log(arr); // [1, 2, foo: 3, bar: 4, '1.1': 5, '-1': 6]

// 프로퍼티는 length에 영향을 주지 않는다.
console.log(arr.length); // 2

 

배열은 객체이기 때문에 배열의 특정 요소를 삭제하기 위해 delete 연산자를 사용할 수 있다. 하지만 length 프로퍼티 값은 변하지 않는다. 완전히 삭제하려면 splice

 

배열 메서드

배열에는 원본 배열을 직접 변경하는 메서드와

원본 배열을 직접 변경하지 않고 새로운 배열을 새성하여 반환하는 메서드가 있다.

 

isArray

전달된 인수가 배열이면 true, 아니면 false

indexOf

인수로 전달된 요소를 검색하여 인덱스 반환. 두번째 인수는 검색을 시작할 인덱스(디폴트는 0)

(includes를 사용하면 가독성 좋다)

모든 요소 찾기

function findValue(arr,value){
    const answer=[];
    let idx=arr.indexOf(value);
    while(idx!==-1){
        answer.push(idx);
        idx=arr.indexOf(value,idx+1)
    }
    return answer;
}

push

인수로 전달받은 모든 값을 마지막 요소로 추가, length 반환. 원본배열 직접 변경

push는 성능 면에서 좋지 않아서 마지막 요소로 추가할 요소가 하나뿐이라면 arr[arr.length]=3 으로 추가하자

배열을 직접 변경하고 싶지 않다면 [...arr,3] 을 이용하자

pop

마지막 요소를 제거하고 제거한 요소를 반환. 직접 변경

push, pop으로 스택 쉽게 구현

class Stack {
  #array; // private class member

  constructor(array = []) {
    if (!Array.isArray(array)) {
      throw new TypeError(`${array} is not an array.`);
    }
    this.#array = array;
  }

  // 스택의 가장 마지막에 데이터를 밀어 넣는다.
  push(value) {
    return this.#array.push(value);
  }

  // 스택의 가장 마지막 데이터, 즉 가장 나중에 밀어 넣은 최신 데이터를 꺼낸다.
  pop() {
    return this.#array.pop();
  }

  // 스택의 복사본 배열을 반환한다.
  entries() {
    return [...this.#array];
  }
}

const stack = new Stack([1, 2]);
console.log(stack.entries()); // [1, 2]

stack.push(3);
console.log(stack.entries()); // [1, 2, 3]

stack.pop();
console.log(stack.entries()); // [1, 2]

 

unsfhit

인수로 전달받은 모든 값을 선두에 요소로 추가. 변경된 length 반환. 직접 변경

shift

첫번째 요소를 제거하고 제거한 요소를 반환

shift와 push로 큐를 쉽게 구현

class Queue {
  #array; // private class member

  constructor(array = []) {
    if (!Array.isArray(array)) {
      throw new TypeError(`${array} is not an array.`);
    }
    this.#array = array;
  }

  // 큐의 가장 마지막에 데이터를 밀어 넣는다.
  enqueue(value) {
    return this.#array.push(value);
  }

  // 큐의 가장 처음 데이터, 즉 가장 먼저 밀어 넣은 데이터를 꺼낸다.
  dequeue() {
    return this.#array.shift();
  }

  // 큐의 복사본 배열을 반환한다.
  entries() {
    return [...this.#array];
  }
}

const queue = new Queue([1, 2]);
console.log(queue.entries()); // [1, 2]

queue.enqueue(3);
console.log(queue.entries()); // [1, 2, 3]

queue.dequeue();
console.log(queue.entries()); // [2, 3]

concat

인수로 전달된 값들을 원본 배열의 마지막 요소로 추가한 새로운 배열을 반환. 원본배열 변경x

spread로 대체 가능

splice

중간에 있는 요소를 제거하거나 변경하는 경우 사용 (start, deleteCount, items)

const arr = [1, 2, 3, 4];

// 원본 배열의 인덱스 1부터 2개의 요소를 제거하고 그 자리에 새로운 요소 20, 30을 삽입한다.
const result = arr.splice(1, 2, 20, 30);

// 제거한 요소가 배열로 반환된다.
console.log(result); // [2, 3]
// splice 메서드는 원본 배열을 직접 변경한다.
console.log(arr); // [1, 20, 30, 4]

특정 요소를 제거하는 경우 indexOf로 인덱스 취득하고 splice 메서드 사용

function remove(array, item) {
  // 제거할 item 요소의 인덱스를 취득한다.
  const index = array.indexOf(item);

  // 제거할 item 요소가 있다면 제거한다.
  if (index !== -1) array.splice(index, 1);

  return array;
}

filter를 사용하여 특정 요소를 제거 가능. 하지만 특정 요소 모두 제거

 

slice

배열 복사. (start, end) start부터 end전의 값 복사 (end는 디폴트가 length)

첫번째 인수가 음수이면 배열의 끝에서부터 요소를 복사

const arr = [1, 2, 3];

// 배열의 끝에서부터 요소를 한 개 복사하여 반환한다.
arr.slice(-1); // -> [3]

// 배열의 끝에서부터 요소를 두 개 복사하여 반환한다.
arr.slice(-2); // -> [2, 3]

얕은 복사라서 깊은 복사를 위해서는 Lodash 라이브러리의 cloneDeep 메서드 추천

 

slice를 이용해서 arguments, HTMLCollection, NodeList 같은 유사 배열 객체를 배열로 변환할 수 있다.

function sum() {
  // 유사 배열 객체를 배열로 변환(ES5)
  var arr = Array.prototype.slice.call(arguments);
  console.log(arr); // [1, 2, 3]

  return arr.reduce(function (pre, cur) {
    return pre + cur;
  }, 0);
}

console.log(sum(1, 2, 3)); // 6

function sum() {
  const arr = Array.from(arguments);
  console.log(arr); // [1, 2, 3]

  return arr.reduce((pre, cur) => pre + cur, 0);
}

console.log(sum(1, 2, 3)); // 6

function sum() {
  // 이터러블을 배열로 변환(ES6 스프레드 문법)
  const arr = [...arguments ];
  console.log(arr); // [1, 2, 3]

  return arr.reduce((pre, cur) => pre + cur, 0);
}

console.log(sum(1, 2, 3)); // 6

 

join

모든 요소를 문자열로 변환한 후, 구분자로 연결한 문자열을 반환. 디폴트는 콤마(,)

reverse

배열 뒤집기

fill

채우기. (채울 값, 시작할 인덱스, 멈출 인덱스)

이런것도 가능

// 인수로 전달받은 정수만큼 요소를 생성하고 0부터 1씩 증가하면서 요소를 채운다.
const sequences = (length = 0) => Array.from({ length }, (_, i) => i);
// const sequences = (length = 0) => Array.from(new Array(length), (_, i) => i);

console.log(sequences(3)); // [0, 1, 2]

Array(5).fill().map((_,i)=>i) // [0, 1, 2, 3, 4]

 

includes

배열내에 특정 요소가 포함되어 있는지 확인. (검색할 대상, 시작 인덱스)

flat

배열의 차원을 줄인다. 디폴트 1, Infinity면 1차원배열

// 중첩 배열을 평탄화하기 위한 깊이 값의 기본값은 1이다.
[1, [2, [3, [4]]]].flat();  // -> [1, 2, [3, [4]]]
[1, [2, [3, [4]]]].flat(1); // -> [1, 2, [3, [4]]]

// 중첩 배열을 평탄화하기 위한 깊이 값을 2로 지정하여 2단계 깊이까지 평탄화한다.
[1, [2, [3, [4]]]].flat(2); // -> [1, 2, 3, [4]]
// 2번 평탄화한 것과 동일하다.
[1, [2, [3, [4]]]].flat().flat(); // -> [1, 2, 3, [4]]

// 중첩 배열을 평탄화하기 위한 깊이 값을 Infinity로 지정하여 중첩 배열 모두를 평탄화한다.
[1, [2, [3, [4]]]].flat(Infinity); // -> [1, 2, 3, 4]

 

배열 고차 함수

고차함수는 함수를 인수로 전달받거나 함수를 반환하는 함수. 불변성 immutable을 지향하는 함수형 프로그래밍에 기반

 

sort

정렬. 디폴트는 오름차순. 역순이면 reverse해주면 돼

숫자 정렬은  points.sort((a, b) => a - b);

객체 정렬

const todos = [
  { id: 4, content: 'JavaScript' },
  { id: 1, content: 'HTML' },
  { id: 2, content: 'CSS' }
];

// 비교 함수. 매개변수 key는 프로퍼티 키다.
function compare(key) {
  // 프로퍼티 값이 문자열인 경우 - 산술 연산으로 비교하면 NaN이 나오므로 비교 연산을 사용한다.
  // 비교 함수는 양수/음수/0을 반환하면 되므로 - 산술 연산 대신 비교 연산을 사용할 수 있다.
  return (a, b) => (a[key] > b[key] ? 1 : (a[key] < b[key] ? -1 : 0));
}

// id를 기준으로 오름차순 정렬
todos.sort(compare('id'));
console.log(todos);
/*
[
  { id: 1, content: 'HTML' },
  { id: 2, content: 'CSS' },
  { id: 4, content: 'JavaScript' }
]
*/

forEach

for문 대신해서 사용. 배열의 모든 요소를 순회하며 콜백 함수를 반복 호출(요소값, 인덱스, this)

원본 배열 변경 가능

const numbers = [1, 2, 3];

// forEach 메서드는 원본 배열을 변경하지 않지만 콜백 함수를 통해 원본 배열을 변경할 수는 있다.
// 콜백 함수의 세 번째 매개변수 arr은 원본 배열 numbers를 가리킨다.
// 따라서 콜백 함수의 세 번째 매개변수 arr을 직접 변경하면 원본 배열 numbers가 변경된다.
numbers.forEach((item, index, arr) => { arr[index] = item ** 2; });
console.log(numbers); // [1, 4, 9]

 

중간에 순회를 중단할 수 없다. for문에 비해 성능이 좋지는 않지만 가독성이 좋다.

 

map

자신을 호출한 배열의 모든 요소를 순회하면서 인수로 전달받은 콜백 함수를 반복 호출한다. 그리고 콜백 함수의 반환값들로 구성된 새로운 배열을 반환한다.(요소값, 인덱스, this)

 

filter

자신을 호출한 배열의 모든 요소를 순회하면서 인수로 전달받은 콜백 함수를 반복 호출한다. 그리고

콜백함수의 반환값이 true인 요소로만 구성된 새로운 배열을 반환(요소값, 인덱스, this)

 

reduce

자신을 호출한 배열을 모든 요소를 순회하며 인수로 전달받은 콜백 함수를 반복 호출. 그리고 콜백 함수의 반환값을 다음 순회 시에 콜백 함수의 첫 번째 인수로 전달하면서 콜백 함수를 호출하여 하나의 결과값을 만들어 반환

(콜백함수(accumulator, currentValue, index, array), 초기값)

 

평균

// 평균
const values = [1, 2, 3, 4, 5, 6];

const average = values.reduce((acc, cur, i, { length }) => {
  // 마지막 순회가 아니면 누적값을 반환하고 마지막 순회면 누적값으로 평균을 구해 반환한다.
  return i === length - 1 ? (acc + cur) / length : acc + cur;
}, 0);

console.log(average); // 3.5

// 요소의 중복 횟수
const fruits = ['banana', 'apple', 'orange', 'orange', 'apple'];

const count = fruits.reduce((acc, cur) => {
  // 첫 번째 순회 시 acc는 초기값인 {}이고 cur은 첫 번째 요소인 'banana'다.
  // 초기값으로 전달받은 빈 객체에 요소값인 cur을 프로퍼티 키로, 요소의 개수를 프로퍼티 값으로
  // 할당한다. 만약 프로퍼티 값이 undefined(처음 등장하는 요소)이면 프로퍼티 값을 1로 초기화한다.
  acc[cur] = (acc[cur] || 0) + 1;
  return acc;
}, {});

// 중복 요소 제거
const values = [1, 2, 1, 3, 5, 4, 5, 3, 4, 4];

const result = values.reduce((acc, cur, i, arr) => {
  // 순회 중인 요소의 인덱스가 자신의 인덱스라면 처음 순회하는 요소다.
  // 이 요소만 초기값으로 전달받은 배열에 담아 반환한다.
  // 순회 중인 요소의 인덱스가 자신의 인덱스가 아니라면 중복된 요소다.
  if (arr.indexOf(cur) === i) acc.push(cur);
  return acc;
}, []);

console.log(result); // [1, 2, 3, 5, 4]
// filter가 더 직관적
const values = [1, 2, 1, 3, 5, 4, 5, 3, 4, 4];

// 순회중인 요소의 인덱스가 자신의 인덱스라면 처음 순회하는 요소이다. 이 요소만 필터링한다.
const result = values.filter((v, i, arr) => arr.indexOf(v) === i);
console.log(result); // [1, 2, 3, 5, 4]
// saet도 가능 추천!!
const values = [1, 2, 1, 3, 5, 4, 5, 3, 4, 4];

// 중복을 허용하지 않는 Set 객체의 특성을 활용하여 배열에서 중복된 요소를 제거할 수 있다.
const result = [...new Set(values)];
console.log(result); // [1, 2, 3, 5, 4]

 

some

자신을 호출한 배열의 요소를 순회하면서 인수로 전달된 콜백 함수를 호출한다. 이때 some 메서드는 콜백 함수의 반환값이 단 한번이라도 참이면 true, 모두 거짓이면 false를 반환한다.

[5, 10, 15].some(item => item > 10); // -> true

every

some과 동작 비슷. 콜백 함수의 반환 값이 모두 참이여야 true

[5, 10, 15].every(item => item > 10); // -> false

find

인수로 전달된 콜백 함수를 호출하여 반환값이 true인 첫번째 요소를 반환

const users = [
  { id: 1, name: 'Lee' },
  { id: 2, name: 'Kim' },
  { id: 2, name: 'Choi' },
  { id: 3, name: 'Park' }
];

// id가 2인 첫 번째 요소를 반환한다. find 메서드는 배열이 아니라 요소를 반환한다.
users.find(user => user.id === 2); // -> {id: 2, name: 'Kim'}

findIndex

위와 비슷한데 인덱스를 리턴하고 존재하지 않으면 -1을 반환한다.

flatMap

map 메서드를 통해 생성된 새로운 배열을 평탄화한다. 즉 map, flat을 순차적으로 실행하는 효과.

 

 

 

728x90
LIST
댓글
공지사항