본문 바로가기

JavaScript

[ JavaScript ] Array Methods 정리, sort 파헤치기

반응형

배열 메소드에 어떤 것이 있는지 한번 쭉 공부해봤다. 다 외우는건 뇌 낭비고(쓰다보면 외워진다) 그냥 어떤 메소드가 있는지 알아두기만 하는 용도로 글을 작성한다. sort는 내용이 조금 길어서 가장 마지막 순서에 적어놨다. 

 

1. map

map은 callback 함수를 각각의 요소에 대해 한번씩 순서대로 불러 그 함수의 반환값으로 새로운 배열을 만든다. 원본 배열이 바뀌지는 않는다. 구문은 아래와 같다. 

 

arr.map(callback(currentValue[, index[, array]])[, thisArg])

 

여기서 [, something] 은 생략 가능하다는 의미다. currentValue만 적고 나머지는 안 쓰는게 일반적이다.

 

thisArg를 제외하고 모든 파라미터를 사용한 예시는 아래와 같다. 아래 예시는 콜벡함수로 넘겨주는 예시라서 인자는 생략된다.

 

function sqrt(curVal, index, ary) {
    // curVal: 원소값 
    // index: curVal의 index 
    // ary: 순회 대상(원본 배열) 

    console.log(curVal); 
    console.log(index); 
    console.log(ary);
    
    return curVal * curVal;
}

const numbers = [1, 2, 3];
const numbers_sqrt = numbers.map(sqrt);

console.log(numbers_sqrt);

 

 

콜벡함수 말고 화살표 함수를 사용하는 경우는 아래와 같다.

 

[1, 2, 3].map((curVal, index, ary) => {
		console.log(curVal);
		console.log(index);
		console.log(ary);
		return curVal * curVal;
}


// 일반적으로 아래와 같이 적는다
[1, 2, 3].map((x) => {
		return x * x
});

// 곧바로 리턴을 하면 아래와 같이 쓸 수 있다
[1, 2, 3].map(x => x * x);

 

 

thisArg는 콜백함수에서 this를 지정해주는 역할을 한다. 언제 쓰는지는 나도 모르겠다. 아래 코드를 보자.

 

function sqrt(val) {
    console.log(this);
    return val * val;
}

before_map = [1,2,3,4]
const maped = before_map.map(sqrt, "this");

console.log(maped);
console.log(before_map);

 

코드를 돌려보면 콜벡함수 내부에서 this가 모두 “this”로 출력됨을 확인할 수 있을 것이다. 만약에 thisArg를 생략하면 콜벡함수 내부의 this는 window 객체가 된다. thisArg는 다른 메소드에서 똑같은 용도로 사용되니 다음 메소드부터는 설명을 생략하겠다.

2. reduce

 

// 구문
arr.reduce(callback(accumulator, curVal, index, arr), initVal]);


// 예제 - 배열의 합 구하기
const ary = [3, 5, 8, 9];

const sum = ary.reduce(function(acc, curVal) {
    return acc + curVal;
}, 0);

console.log(sum);

 

  • accumulator : 콜백의 반환값을 누적.
  • initVal : accumulator에 제공하는 누적 초기값.
  • initVal을 제공한 경우 : accumulator = initVal,  curVal = arr[0]
  • initVal을 제공하지 않은 경우 : accumulator = arr[0],  curVal = arr[1]
  • 최종 반환 이전에는 반환값이 accumulator로 넘어간다.
  • 되도록이면 initVal을 설정해주는게 안전하다.

3. filter

// 구문
arr.filter(callback(element[, index[, array]])[, thisArg])


// 인덱스가 2 이하인 값만 가져오는 예제
const ary = [1, 3, 4, 5, 6];

const filtered = ary.filter((val, index) => {
    return index <= 2;
})

console.log(filtered);

 

  • filter는 배열 내 각 요소에 대해 callback 함수가 true를 반환하면 요소를 유지하고, false를 반환하면 버린다.
  • filter의 콜벡함수는 반환값이 boolean 타입이어야 한다.

4. from

// 구문
Array.from(arrayLike[, mapFn[, thisArg]])


// 예제
Array.from("cat");
// ['c', 'a', 't']


// 0으로 초기화된 20x10 짜리 2차원 배열 만들기
Array.from({length: 20}, () => Array(10).fill(0));

 

  • 유사배열에 배열 메소드를 적용시킬때 쓴다.
  • 유사배열로는 String, 객체, NodeList, arguments 등이 있다.

근데 여기서 생기는 의문점이 있다. Array.from({ length: 20 }) 이 어떻게 배열이 될까? 과정을 한번 살펴보자. 간단한 실습을 통해 이해할 수 있다. 먼저 개발자 도구에서 콘솔창을 열어보자. 그리고 아무 배열을 만든 다음에 그 배열을 찍어보자. 아래와 같이 말이다. 

 

 

배열 아래쪽에 표시되는 값들이 유사배열 객체다. 배열의 인덱스와 값이 key-value 형태로 있으며, 마지막에는 length 값이 있는 것을 확인할 수 있다. { length: 20 } 또한 유사배열 객체다. 다만 인덱스와 값에 대한 정보가 없을 뿐이다. 이 상태로 Array.from() 안으로 들어가면 값에 정보 없이 '길이가 20'이라는 정보만 있으므로 배열의 값으로 undifined가 자동으로 들어가게 된다. 그렇다면 아래의 객체가 Array.from() 안에 들어간다면 어떤 배열이 될까? 혼자 예측해보고, 더보기란을 클릭해서 정답을 확인해보자. 

 

const obj = {
    1: 3,
    b: 24,
    2: 6,
    length: 5
};

Array.from(obj);
더보기

인덱스로 접근 가능한 값에 대해서는 배열에 그대로 반영된다. 먼저 length 값은 5이므로 길이가 5인 배열이 생성된다. 인덱스로 접근 가능한 곳이 1, 2 이므로 배열의 두번째, 세번째 값에는 3, 6이 들어가고 나머지는 undefined가 된다. 

 

 

(추가) 자바스크립트에서 배열도 객체로 분류된다. 그래서 배열에 typeof를 쓰면 'Object'가 출력된다. 배열과 유사배열을 구분하기 위해서는 아래 메소드를 사용해야 한다.

Array.isArray([1,2,3]) // true
Array.isArray('123')   // false

5. fill

// 정의
arr.fill(value[, start[, end]])


// 예제 - 0으로 채워진 1x30 배열 생성
Array(30).fill(0)

 

  • value: 배열을 채울 값
  • start: 시작 인덱스, 기본값은 0
  • end: 끝 인덱스, 기본값은 this.length

 

 

 

concat - 배열 이어 붙이기

 

// 구문
array.concat([value1[, value2[, ...[, valueN]]]])


// 예시
const num1 = [1, 2, 3];
const num2 = [4, 5, 6];
const num3 = [7, 8, 9];

num1.concat(num2, num3);
// 결과: [1, 2, 3, 4, 5, 6, 7, 8, 9]

 

 

 

flat - 중첩 배열 평탄화

 

// 구문
const newArr = arr.flat([depth])


// 예제
const arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]

const arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]

const arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

 

 

 

join - 배열의 요소를 하나의 문자열로 병합

 

// 구문
arr.join([separator])


// 예제
const a = ['바람', '비', '불'];
a.join();      // '바람,비,불'
a.join(', ');  // '바람, 비, 불'
a.join(' + ');   // '바람 + 비 + 불'
a.join('');      // '바람비불'

 

 

 

every - 배열의 전체 요소가 기준을 만족하는지 검사

 

// 구문
arr.every(callback(element[, index[, array]])[, thisArg])


// 예제 : 배열의 모든 원소가 40 미만인지?
const ary = [1, 30, 39, 29, 10, 13];

console.log(ary.every((curVal) => curVal < 40));

 

 

 

some - 배열 원소중 하나라도 기준을 만족하는지 검사

 

// 구문
arr.some(callback(element[, index[, array]])[, thisArg])


예제 : 배열 안에 30보다 큰 숫자가 존재하는지?
const ary = [1, 4, 6, 44, 3, 14];

console.log(ary.some((val) => val > 30));

 

 

 

  • Array.includes(value) : 배열에 value가 존재하면 true, 아니면 false 반환
  • Array.indexOf(value[, fromIndex]) : 배열에서 value 인덱스 반환. 존재하지 않으면 -1 반환. fromIndex부터 탐색.
  • Array.lastIndexOf(value[, fromIndex]) : fromIndex을 시작으로 뒤로 탐색
  • forEach - 반환값이 없는 map
  • reverse - 배열 뒤집기. 원본이 수정되니깐 주의!
  •  

 

 

 

(추가1) 배열에서 최대, 최소값 구하기

const ary = [4,2,66,35,75,13,83];

console.log(Math.max(...ary)); // 최대값
console.log(Math.min(...ary)); // 최소값

 

Sort

자바스크립트 정렬방식은 MDN 웹문서를 보면 아주 자세하게 나와있다. 그런데 봐도 헷갈린다. 그래서 내 나름대로 이해하기 쉬운 방식으로 다시 정리해봤다. 우선 정석적인 방법을 먼저 보자. MDN 웹문서에 있는 내용이다.

 

// 구문
arr.sort([compareFunction])
  • compareFunction(a, b) > 0 이면 a,b 순서를 바꾼다.
  • compareFunction(a, b) < 0 이면 그대로 놔둔다.
  • compareFunction(a,b) == 0 이면 그대로 놔둔다.

여기서 핵심은 compareFunction(a, b) 의 반환값이 양수일 때 순서를 바꾼다는 것이다. 이것만 기억해놓자.

 

이제 내 방식을 설명하겠다. 임의의 배열을 생성하고, sort 내부에서 (a, b) 순서로 콘솔에 찍어보자. 아래 코드를 복붙해서 돌려보기를 바란다.

 

const nums = [4,2];

nums.sort((a, b) => {
    console.log(a, b);
});

// 결과 : 2 4

 

나는 처음에 당연히 (4, 2)가 출력될 줄 알았다. 그런데 출력 결과를 보면 순서가 바뀐 것을 알 수 있다. 이는 sort가 배열을 뒤집어서 비교한다는 것을 의미한다. 따라서 파라미터의 순서를 원래 배열과 일치시키기 위해서는 (a, b)가 아닌 (b, a)로 써줘야 한다.  만약에 내부가 a > b 이면 내림차순이고, a < b 이면 오름차순이다. 함수 내부는 양수가 되는 규칙을 따르게 된다. 따라서 아래가 성립한다.

 

a - b 를 반환하면 내림차순
b - a 를 반환하면 오름차순

 

단, 위 규칙은 비교 대상의 자료형이 Number일때만 성립한다. 문자열을 정렬하고자 할때는 대소비교를 해줘서 1, 0, -1중 하나를 반환해줘야 한다. 양수가 되는 규칙을 따르는 것만 잘 기억하면 어렵지 않다. 만약에 내림차순으로 정렬하고자 한다면 a>b 를 1로 반환하면 되고, 오름차순으로 정렬하고자 한다면 a<b를 1로 반환하면 된다.  

 

 

아래 예제들를 참고해서 이해해보도록 하자. 결과를 보면 알겠지만 sort 함수는 원본 배열을 수정한다. 

 

문자열 배열 정렬

const str = ["cat", "dog", "pig", "rabbit", "mouse"];

// 1. 문자열 내림차순 정렬
str.sort((b, a) => {
    if (a > b) return 1;
    else if(a < b) return -1;
    return 0; 
})
console.log(str);
// [ 'rabbit', 'pig', 'mouse', 'dog', 'cat' ]


// 2. 문자열 오름차순 정렬
str.sort((b, a) => {
    if(a < b) return 1;
    else if(a >= b) return -1;
    return 0;
})
console.log(str);
// [ 'cat', 'dog', 'mouse', 'pig', 'rabbit' ]

 

객체 정렬

const student = [
    {id: 2, age: 26, name: 'Ron'},
    {id: 28, age: 25, name: 'Sony'},
    {id: 6, age: 27, name: 'Wayne'},
    {id: 17, age: 24, name: 'Charles'}
]


// 1. id 기준 오름차순 정렬
student.sort((b, a) => b.id - a.id);
console.log(student);
// id 순서 = [2, 6, 17, 28]


// 2. id 기준 내림차순 정렬
student.sort((b, a) => a.id - b.id);
console.log(student);
// id 순서 = [28, 17, 6, 2]


// 3. name 기준 오름차순 정렬
student.sort((b, a) => {
    if(a.name < b.name) return 1;
    else if(a.name > b.name) return -1;
    return 0;
})
console.log(student);
// name 순서 = ['Charles', 'Ron', 'Sony', 'Wayne']


// 4. name 기준 내림차순 정렬
student.sort((b, a) => {
    if(a.name > b.name) return 1;
    else if(a.name < b.name) return -1;
    return 0;
})
console.log(student);
// name 순서 = ['Wayne', 'Sony', 'Ron', 'Charles']

 

 

 

<참조>

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array

https://stackoverflow.com/questions/35142983/understanding-arr-filtercallback-thisarg-syntax 

 

 

반응형