Javascript

다시 시작하는 자바스크립트 - 배열

min' 2023. 3. 20. 01:42
728x90
반응형

 

헷갈리는 것들만 따로 정리할 예정.
deep dive 책을 함께 공부함.
 
 
 

9. 배열 (Array)

 

요즘에는 특정한 데이터 타입만 넣을 수 있는 Array를 만들 수 있음.

이렇게 데이터 타입이 지정된 배열을 쓰면 그 외에 데이터 타입을 넣게 되면 에러가 발생.

ex : Int8Array, Unit8Array, Float32Array, Float64Array 등...

 

 

9-1) 배열 생성 방법

 

[1] 생성자 함수

let array = new Array(2);
// 사이즈를 지정 가능
// 2개의 비어있는 아이템이 생김
console.log(array);
// [ <2 empty items> ]

array = new Array(1, 2, 3);
// 내가 만들고 싶은 아이템을 전달해도 됨
console.log(array);
// [ 1, 2, 3 ]

 

[2] static 함수 Array.of()

array = Array.of(1, 2);
// 요소들의 집합으로부터 새로운 배열을 반환
console.log(array);
// [ 1, 2 ]

 

[3] 배열 리터럴

const anotherArray = [1, 2, 3, 4];
console.log(anotherArray);
// [ 1, 2, 3, 4 ]

 

[4] static 함수 Array.from()

array = Array.from(anotherArray);
// anotherArray 변수에 담긴 배열을 복사해서 새로운 배열로 반환.
console.log(array);
// [ 1, 2, 3, 4 ]

array = Array.from("TEXT");
// from은 새로운 array를 만드는데 iterable object로부터 iterable을 전달 받음.
// iterable은 순회가 가능한 모든 것을 전달할 수 있음.
// 문자열도 순회가 가능하므로 iterable로 문자열을 전달할 수도 있음.
console.log(array);
// [ 'T', 'E', 'X', 'T' ]

 

일반적으로 array는 동일한 메모리 크기를 가지며, 연속적으로 이어져 있어야 함.
but, 자바스크립트의 array는 연속적으로 이어져 있지 않고 object와 유사.
자바스크립트에서의 array는 일반적인 array의 동작을 흉내넨 특수한 object.
=> 이걸 보완하기 위해서 type이 정해져 있는 type array가 있음 (Typed Collections)

 

❗ object로부터 배열 만들기

array = Array.from({
  0 : "안",
  1 : "녕",
  length : 2,
});

// object로부터 배열을 만들기 위해서는 object에 두 가지 종류의 키를 넣어주면 됨.
// 첫 번 째는 원하는 아이템 값을 넣어주기 위한 index key.
// 두 번 째는 몇 개의 아이템을 넣을 것인지 표기할 length key.

console.log(array);
// [ "안", "녕" ]

 

 

9-2) 배열 접근 및 아이템 추가, 삭제 방법

 

[1] 배열에 접근

const fruits = ["🍌", "🍎", "🍇", "🍑"];

console.log(fruits[0]); // 🍌
console.log(fruits[1]); // 🍎
console.log(fruits[2]); // 🍇
console.log(fruits[3]); // 🍑

// index로 배열에 접근하여 아이템 값을 얻을 수 있음.

console.log(fruits.length);
// instance property(속성) length를 사용하여 배열의 길이를 값으로 얻을 수 있음.

 

[2] 반복문을 통한 배열 순회

for(let i = 0; i < fruits.length; i++){
  console.log(fruits[i]);
}

// 🍌
// 🍎
// 🍇
// 🍑

// 배열에 여러 번 접근할 필요 없이 for문 한 번으로 배열의 아이템 값을 차례대로 얻어낼 수 있음.

 

[3] 아이템 추가, 삭제

 

3-1) 좋지 못한 방식

index로 배열의 아이템에 바로 접근하는 것은 좋지 못한 방식임.

fruits[3] = "🍓";
console.log(fruits);
// ["🍌", "🍎", "🍇", "🍑", "🍓"]

fruits[fruits.length] = "🍓";
// index.length로 추가하게 되면 맨 마지막에 넣을 수 있지만 역시 좋은 방식이 아님
console.log(fruits);
// [ '🍌', '🍎', '🍇', '🍓', '🍓' ]

delete fruits[1];
console.log(fruits);
// [ '🍌', <1 empty item>, '🍇', '🍓', '🍓' ]
// delete을 이용해서 index에 접근해 아이템을 삭제할 수 있음
// 이렇게 비우게 되면 그 자리가 empty item으로 비어있게

 

3-2) 아이템 추가, 삭제를 위한 array method

// array method를 사용할 때 가장 중요하게 봐야 하는 것은
// 배열 자체를 변경하는 것인지, 새로운 배열을 반환하는 것인지임.

const fruits = ["🍌", "🍎", "🍇"];


[1] 기존의 배열이 수정되는 배열 메소드 :

=> push - 새로운 요소를 배열 맨 끝에 추가한 후에 배열의 길이를 return

let length = fruits.push("🍉");
console.log(fruits);
// ["🍌", "🍎", "🍇", "🍉"];
console.log(length); 
// 4

=> unshift - 새로운 요소를 배열 맨 앞에 추가한 후에 배열의 길이를 return

let length = fruits.unshift("🍰");
console.log(fruits);
// ["🍰" , "🍌", "🍎", "🍇", "🍉"];
console.log(length);
// 5

=> pop - 배열 맨 뒤 요소를 제거하고 제거된 아이템을 return해줌

let lastItem = fruits.pop();
console.log(fruits); 
// [ '🍰', '🍌', '🍎', '🍇' ]
console.log(lastItem);
// 🍉

=> shift - 배열 맨 앞 요소를 제거하고 제거된 아이템을 return해줌

lastItem = fruits.shift();
console.log(fruits);
// [ '🍌', '🍎', '🍇' ]
console.log(lastItem);
// 🍰

=> splice - 중간에 추가 또는 삭제하고 삭제한 아이템을 반환함.
     인자 : 시작하는 인덱스, 삭제할 갯수(옵션), 삭제하고 다시 추가할 아이템(옵션).

[1]
const deleted = fruits.splice(1, 1);
console.log(fruits); 
// [ '🍌', '🍇' ]
console.log(deleted);
// [ '🍎' ]

[2]
fruits.splice(1, 0, "🍗");
// 아무것도 삭제하지 않고 1번째 자리에 "🍗"를 추가
// 하나가 아니라 여러 개를 추가해줄 수도 있음
console.log(fruits);
// [ '🍌', '🍗', '🍇' ]


=> fill - 특정한 값으로 배열 채우기
     인자 : 어떤 것을 채울 건지, 어느 인덱스에서 시작할 것인지(옵션), 어느 인덱스 전에서 끝을 맺을 건지(옵션).

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

[1]
arr.fill(0);
console.log(arr);
// 0으로 배열을 다 채워줌
// [ 0, 0, 0, 0, 0, 0, 0, 0 ]

[2]
arr.fill("s", 1, 3);
console.log(arr);
// 's'로 1부터 3 전까지 채우기
// [ 0, 's', 's', 0, 0, 0, 0, 0 ]

[3]
arr.fill("n", 1);
console.log(arr);
// 'n'로 1부터 전체 다 채우기
// [ 0, 'n', 'n', 'n', 'n', 'n', 'n', 'n' ]


[2] 새로운 배열을 반환하는 배열 메소드 :

=> slice - 기존에 배열을 유지한 상태로 잘라진 새로운 배열을 return.
     인자 : 어느 인덱스에서 시작할 것인지(옵션), 어느 인덱스 전에서 끝을 맺을 건지(옵션).

const fruits = [ '🍌', '🍗', '🍇' ];

[1]
let newArr = fruits.slice(0, 2);
console.log(fruits);
// 기존의 배열은 계속 유지됨.
// [ '🍌', '🍗', '🍇' ]
console.log(newArr);
// 0부터 2 전까지 잘라진 것들을 출력
// [ '🍌', '🍗' ]

[2]
newArr = fruits.slice();
// 아무것도 지정해주지 않으면 전체가 반환
console.log(fruits);
// [ '🍌', '🍗', '🍇' ]
console.log(newArr);
// [ '🍌', '🍗', '🍇' ]

[3]
newArr = fruits.slice(-1);
console.log(fruits);
// [ '🍌', '🍗', '🍇' ]
console.log(newArr);
// 맨 뒤에서 한 칸 앞 당긴 것만 잘라내서 반환
// [ '🍇' ]

=> concat - 여러 개의 배열을 붙여서 새로운 배열로 return

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = arr1.concat(arr2);

console.log(arr1);
// [1, 2, 3]
console.log(arr2);
// [4, 5, 6]
console.log(arr3);
// [1, 2, 3, 4, 5, 6]

=> reverse - 순서를 거꾸로해서 새로운 배열을 return

const arr4 = arr3.reverse();
console.log(arr4);
// [ 6, 5, 4, 3, 2, 1 ]

=> flat - 중첩 배열을 하나씩 배열로 쫙 펴서 새로운 배열로 return

let flatArray = [
  [1, 2, 3],
  [4, [5, 6, [3, 4]]],
];

[1]
console.log(flatArray);
console.log(flatArray.flat());
// [ 1, 2, 3, 4, [ 5, 6, [ 3, 4 ] ] ]

[2]
console.log(flatArray.flat(2));
// 1단계까지 중첩된 것을 풀어주기 때문에
// 2단계까지 중첩된 것을 풀어주고 싶다면 ()안에 2를 넣어줘야 함
// [ 1, 2, 3, 4, 5, 6, [ 3, 4 ] ]

[3]
flatArray = flatArray.flat(3);
console.log(flatArray);
// 3단계까지 중첩된 것을 풀어줌.
// [ 1, 2, 3, 4, 5, 6, 3, 4 ]


[3] 배열을 문자열로 합하는 배열 메소드 join

const arr = [ 0, 'n', 'n', 'n', 'n', 'n', 'n', 'n' ];

[1]
let text = arr.join();
console.log(text);
// 0, n, n, n, n, n, n, n

[2]
text = arr.join("|");
console.log(text);
// 콤마 말고 다른 식으로 문자열을 합하고 싶다면
// 0 | n | n | n | n | n | n | n

 

 

9-3) Shallow copy(얕은 복사)

 

Shallow copy - 객체를 복사하게 되면 메모리 주소를 전달하게 되는 것을 의미.자바스크립트에서 복사를 할 때는 항상 Shallow copy가 이뤄짐.=> Array.from, concat, slice, spread(...), Object.assign

 

function(obj){  인자로 전달된 object가 있다면 그것을 함수 내부에서 수정하는 것은 위험함.  왜냐하면 기존의 object까지 수정되기 때문.}

 

const pizza = { name: "🍕", price: 2, owner: { name: "Ellie" } };
const ramen = { name: "🍲", price: 3 };
const sushi = { name: "🍣", price: 1 };


const store1 = [pizza, ramen];
const store2 = Array.from(store1);


console.log("store1", store1);

// store1 [ { name: '🍕', price: 2, owner: { name: 'Ellie' } }, { name: '🍲', price: 3 } ]
console.log("store2", store2);

// store2 [ { name: '🍕', price: 2, owner: { name: 'Ellie' } }, { name: '🍲', price: 3 } ]

 

store2.push(sushi);
console.log("store1", store1);
// store1 [ { name: '🍕', price: 2, owner: { name: 'Ellie' } }, { name: '🍲', price: 3 } ]

console.log("store2", store2);
// store1 [ { name: '🍕', price: 2, owner: { name: 'Ellie' } }, { name: '🍲', price: 3 }, { name: '🍣', price: 1 } ]

// store1, store2 둘 다 각각 새로 만들어진 배열을 가리킴 ( 서로 다른 레퍼런스 주소)
// 서로 다른 배열이기 때문에 store2에만 sushi를 추가하면 store1에는 영향이 가지 않음

 

but, 참조하고 있는 object 자체를 수정한다는 것은 얘기가 다름.

 

pizza.price = 4;
console.log("store1", store1);
// store1 [ { name: '🍕', price: 4, owner: { name: 'Ellie' } }, { name: '🍲', price: 3 } ]

console.log("store2", store2);
// store2 [ { name: '🍕', price: 4, owner: { name: 'Ellie' } }, { name: '🍲', price: 3 }, { name: '🍣', price: 1 } ]

// store1을 만들 때 pizze, ramen 배열의 주소를 복사해오는
// 동일한 오브젝트를 가지고 있기 때문에 오브젝트를 수정한다면 store1과 stroe2도 같이 수정

 

 

9-4) Higher-order function(고차함수)

 

고차함수에 관하여 알아보기 전에, 일급함수에 관하여 알아보겠음.

 

일급함수(first-class function) - 함수가 일반 객체처럼 모든 연산이 가능한 것.

  1. 함수의 매개변수로 전달
  2. 함수의 반환값
  3. 할당 명령문
  4. 동일 비교 대상

 

그렇다면, Higher-order function(고차함수)은 무엇인가?

 

고차함수(Higher-order function) - 인자로 함수를 받거나(콜백함수) 함수를 반환하는 함수

  1. 배열에서도 사용할 수 있는 유용한 고차함수들이 많음.
  2. 서로 특정한 일을 하는 함수들끼리 엮어놓은 것함수형 프로그래밍이라고 함.
  3. 함수형 프로그래밍을 하기 위해서는 함수 자체를 순수함수(함수 안에서 불변성 유지)로 만드는 것이 중요.
  4. 순수함수로 만듦으로써 error는 줄이고 가독성은 높일 수 있음.
  5. 함수형 프로그래밍을 사용 - 데이터 변경 X(불변성 유지), 변수 사용 X, 조건문 X, 반복문 X

 

이제 배열의 고차함수에는 무엇이 있는지 알아보겠음.

 

const fruits = ["🍌", "🍓", "🍇", "🍓"];

for (let i = 0; i < fruits.length; i++) {
  console.log(fruits[i]);
}

[1] 배열을 빙글 빙글 돌면서 원하는 것(콜백함수)을 할 때

=> forEach 함수
     인자 : callback 함수를 받음 => callback 함수의 인자로는 value, index, array를 받음.

fruits.forEach(function (value, index, array) {
  console.log("----------------------------");
  console.log(value); // value만 사용할 거라면 나머지 인자들은 생략이 가능
  console.log(index);
  console.log(array);
});

// ----------------------------
// 🍌
// 0
// [ '🍌', '🍓', '🍇', '🍓' ]
// ----------------------------
// 🍓
// 1
// [ '🍌', '🍓', '🍇', '🍓' ]
// ----------------------------
// 🍇
// 2
// [ '🍌', '🍓', '🍇', '🍓' ]
// ----------------------------
// 🍓
// 3
// [ '🍌', '🍓', '🍇', '🍓' ]
// 🍌
// 🍓
// 🍇
// 🍓

fruits.forEach((value) => console.log(value));
// 🍌
// 🍓
// 🍇
// 🍓


[2] 조건에 맞는(콜백함수) 아이템을 찾을 때

=> find : 제일 먼저 조건에 맞는 아이템을 return

const item1 = { name: "🥛", price: 2 };
const item2 = { name: "🍪", price: 3 };
const item3 = { name: "🍙", price: 1 };
const products = [item1, item2, item3, item2];

let result = products.find((value) => value.name === "🍪");
console.log(products);
// [ { name: '🥛', price: 2 }, { name: '🍪', price: 3 }, { name: '🍙', price: 1 }, { name: '🍪', price: 3 } ]
console.log(result);
// { name: '🍪', price: 3 }

=> findIndex : 제일 먼저 조건에 맞는 아이템의 인덱스를 return

result = products.findIndex((value) => value.name === "🍪");
console.log(result);
// 1

=> some : 배열의 아이템들이 부분적으로 조건(콜백함수)에 맞는지 확인

result = products.some((value) => value.name === "🍪");
// 배열 이름이 하나라도 쿠키면 true가 나오도록
console.log(result);
// true

=> every :  배열의 아이템들이 전부 조건(콜백함수)에 맞는지 확인

result = products.every((value) => value.name === "🍪");
// 배열 이름이 전체가 쿠키면 true가 나오도록
console.log(result);
// false

=> filter : 조건에 맞는 모든 아이템들을 새로운 배열로 return.

result = products.filter((item) => item.name === "🍪");
console.log(result);
// [ { name: '🍪', price: 3 },{ name: '🍪', price: 3 } ]


[3] 아이템들을 각각 다른 아이템으로 매핑

=> map : 배열의 아이템들을 다른 아이템으로 매핑하여 새로운 배열을 return.

const nums = [1, 2, 3, 4, 5];

[1]
result = nums.map((item) => item * 2);
console.log(result);
// [ 2, 4, 6, 8, 10 ]

[2]
result = nums.map((item) => {
  if (item % 2 === 0) {
    return item * 2;
  } else {
    return item;
  }
});

console.log(result);
// [ 1, 4, 3, 8, 5 ]

=> flatMap : map을 하는 대상자가 배열이라면 배열을 flat하게 쫙 펴준 후 새로운 배열을 return.

result = nums.map((item) => [1, 2]);
console.log(result);
// [ [ 1, 2 ], [ 1, 2 ], [ 1, 2 ], [ 1, 2 ], [ 1, 2 ] ]

[1]
result = nums.flatMap((item) => [1, 2]);
console.log(result);
// [ 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 ]

- flatMap 예시

result = ["dream", "coding"].map((text) => text.split(""));
console.log(result);
// map을 사용하면 배열 안에 있는 배열이 펴지지 않으므로
// [ ['d', 'r', 'e', 'a', 'm' ], [ 'c', 'o', 'd', 'i', 'n', 'g' ] ]
// split 메소드를 이용하여 ""를 기준으로 나누고 배열에 담아줌.

[2]
result = ["dream", "coding"].flatMap((text) => text.split(""));
console.log(result);
// 글자들이 한 줄로 분리되어 쫙 펴짐
// [ 'd', 'r', 'e', 'a', 'm', 'c', 'o', 'd', 'i', 'n', 'g' ]


[4] 배열의 아이템들을 정렬

=> sort : 문자열 형태의 오름차순으로 요소를 정렬하고, 기존의 배열을 변경

const texts = ["hi", "abc"];
texts.sort();
console.log(texts);
// [ 'abc', 'hi' ]

const numbers = [0, 5, 4, 2, 1, 10];
numbers.sort();
console.log(numbers);
// [ 0, 1, 10, 2, 4, 5 ] => error : 10은 앞에 1이 있어 1 다음에 나오게 됨

// < 0 : a가 앞으로 정렬, 오름차순
// > 0 : b가 앞으로 정렬, 내림차순

numbers.sort((a, b) => a - b);
// 숫자로 변환하여 정렬하고 싶을 때는 콜백함수를 써야 함
// 앞 - 뒤를 했을 때 마이너스가 되어야만 함 => 10이 1 뒤에 올 수도, 2 앞에 올 수도 없음

console.log(numbers);
// [ 0, 1, 2, 4, 5, 10 ]


[5] 배열의 아이템들을 접어서 값을 하나로 만들어줌.

=> reduce : 배열의 요소들을 접어서 값을 하나로 만들어 return.
     인자 : callback 함수를 받음 => callback 함수의 인자로는
     
previousValue, currentValue, currentIndex, array를 받음.

// reduce의 콜백함수 인자로 계속 더해지는 값을 저장할 sum과 각각의 요소들을 전달받을 value를 넣어줌

result = [1, 2, 3, 4, 5].reduce((sum, value) => (sum += value), 0);
console.log(result);
// 15

// result = [1,2,3,4,5].reduce((sum,value) =>
// {
//  sum += value;
//  return sum; => return sum += value;와 같은데 바로 return할 경우 중괄호와 return 생략 가능.
// }, 0);
// 0과 같이 처음에 시작할 값을 넣어줄 수 있음
728x90
반응형