오늘은 영하 8도였네요.. 진짜 어마무시한 추위인것 같아요 요즘! 다들 감기 조심하세요..ㅜ 🥶
오늘은 Javascript 30 의 4번째 주제를 가지고 포스팅 해보려고 해요!
Day04 project는 이전의 게시물 보다는 약간 정적이고 눈에 보이는 것이 아니라 지루할 수도 있지만, 누가봐도 너무 유용하게 쓰일 것이라..! 잘 정리해보겠습니다 🙃
배열에 관한 여러가지 연산 (
filter
,map
,sort
,reduce
) 에 대해 연습해본다.
먼저, 이번 프로젝트에서는 크게 2가지 배열을 먼저 제공 받았습니다.
배열 내용은 다음과 같아요.
const inventors = [
{ first: 'Albert', last: 'Einstein', year: 1879, passed: 1955 },
{ first: 'Isaac', last: 'Newton', year: 1643, passed: 1727 },
{ first: 'Galileo', last: 'Galilei', year: 1564, passed: 1642 },
{ first: 'Marie', last: 'Curie', year: 1867, passed: 1934 },
{ first: 'Johannes', last: 'Kepler', year: 1571, passed: 1630 },
{ first: 'Nicolaus', last: 'Copernicus', year: 1473, passed: 1543 },
{ first: 'Max', last: 'Planck', year: 1858, passed: 1947 },
{ first: 'Katherine', last: 'Blodgett', year: 1898, passed: 1979 },
{ first: 'Ada', last: 'Lovelace', year: 1815, passed: 1852 },
{ first: 'Sarah E.', last: 'Goode', year: 1855, passed: 1905 },
{ first: 'Lise', last: 'Meitner', year: 1878, passed: 1968 },
{ first: 'Hanna', last: 'Hammarström', year: 1829, passed: 1909 }
];
const people = [
'Bernhard, Sandra', 'Bethea, Erin', 'Becker, Carl', 'Bentsen, Lloyd', 'Beckett, Samuel', 'Blake, William', 'Berger, Ric', 'Beddoes, Mick', 'Beethoven, Ludwig',
'Belloc, Hilaire', 'Begin, Menachem', 'Bellow, Saul', 'Benchley, Robert', 'Blair, Robert', 'Benenson, Peter', 'Benjamin, Walter', 'Berlin, Irving',
'Benn, Tony', 'Benson, Leana', 'Bent, Silas', 'Berle, Milton', 'Berry, Halle', 'Biko, Steve', 'Beck, Glenn', 'Bergman, Ingmar', 'Black, Elk', 'Berio, Luciano',
'Berne, Eric', 'Berra, Yogi', 'Berry, Wendell', 'Bevan, Aneurin', 'Ben-Gurion, David', 'Bevel, Ken', 'Biden, Joseph', 'Bennington, Chester', 'Bierce, Ambrose',
'Billings, Josh', 'Birrell, Augustine', 'Blair, Tony', 'Beecher, Henry', 'Biondo, Frank'
];
약간 복잡해보이지만, 크게 보면 inventors
배열과 people
배열 두 개를 가지고 이런저런 함수를 써보는 시간을 가져봤어요!
하나하나 문제로 나왔던 부분을 차례로 설명드릴게요.
첫번째 질문은 1500년대에 태어난 (year
가 1500이상 1600미만) 사람을 필터링 하는 질문이였습니다.
MDN
에 따르면, filter
의 정의는 아래와 같습니다.
filter() 메서드는 주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환합니다
또한, 기본적인 사용시 골격은
let results = arr.filter(function(item, index, array) {
// 조건을 충족하는 요소는 results에 순차적으로 더해집니다.
// 조건을 충족하는 요소가 하나도 없으면 빈 배열이 반환됩니다.
});
filter
의 특징은 배열을 반환한다는 점인 것 같아요!
더 세부적인 예시를 들어볼게요.
let users = [
{id: 1, name: "John"},
{id: 2, name: "Pete"},
{id: 3, name: "Mary"}
];
// 앞쪽 사용자 두 명을 반환합니다.
let someUsers = users.filter(item => item.id < 3);
alert(someUsers.length); // 2
objects가 있는 users
배열에 화살표 함수 item => item.id < 3
를 사용하여 각각 원소의 id
값이 3 미만인 객체들을 필터링 하는 모습입니다.
따라서 someUsers
에는 id
가 1, 2 인 객체가 들어간 object
배열을 받게됩니다.
const elder = inventors.filter(person => person.year >= 1500 && person.year < 1600);
간단하게 해결했습니다! 😄 filter
함수를 사용해 각각 객체를 person
으로 보고 각 객체의 year
속성에 접근했습니다.
다음 질문! inverntors
배열에서 firstName
과 lastName
을 리턴하는 것이 요구사항이였습니다.
이는 새로운 배열을 만드는 map
메소드를 사용했는데요, 그 정의 및 예제를 잠깐살펴볼게요.
MDN
에 따르면, map
의 정의는 아래와 같습니다.
map()
메서드는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환합니다.
주목해야 할 점은 새로운 배열을 반환하는 점인데요, 정확한 예시를 보여드릴게요.
const array1 = [1, 4, 9, 16];
// pass a function to map
const map1 = array1.map(x => x * 2);
console.log(map1);
// expected output: Array [2, 8, 18, 32]
기존에 존재하는 array1
이라는 배열을 기반으로 map1
배열을 새로 생성했습니다!
대신, array1
배열의 모든 원소에 2씩 증가시켜 새로운 배열을 생성한 모습을 확인 할 수 있어요.
const inventorsFirst = inventors.map(x => x.first);
const inventorsLast = inventors.map(x => x.last);
간단합니다! 익명함수를 화살표로 사용하여 inventorsFirst
와 inventorsLast
배열에 각각 first
와 `last 를 저장했습니다. 😺
다음 질문은 sort
항목이였습니다..!
사실 이게.. 진짜 빠삭하게 이해가 가질 않아서.. 일단은 제가 이해한 내용을 기반으로 작성해보겠습니다.
MDN
에 따르면, sort
의 정의는 아래와 같습니다.
sort()
메서드는 배열의 요소를 적절한 위치에 정렬한 후 그 배열을 반환합니다. 정렬은 stable sort가 아닐 수 있습니다. 기본 정렬 순서는 문자열의 유니코드 코드 포인트를 따릅니다.
가장 주목해야할 부분은, 문자열의 유니코드 코드 포인트 인것 같아요! 즉, 기본적인 소팅 순서는 문자열의 크기라는 뜻이죠!
const months = ['March', 'Jan', 'Feb', 'Dec'];
months.sort();
console.log(months);
// expected output: Array ["Dec", "Feb", "Jan", "March"]
const array1 = [1, 30, 4, 21, 100000];
array1.sort();
console.log(array1);
// expected output: Array [1, 100000, 21, 30, 4]
해당 웹사이트에 나와있는 예시입니다. months
는 기본적으로 문자열들이 들어가 있고, 이를 소팅하면 문자열 순서대로 소팅이 됨을 알 수 있어요.
하지만 array1
를 보시면 흔히 숫자 소팅하듯이 결과가 1, 4, 21, 30, 10000
가 아닌, 문자열대로 소팅 한 것을 확인 하실 수 있습니다.
이런 소팅은 c++ 에서 벡터를 소팅할때 조건함수를 쓰듯이 조건을 걸수가 있어요.
바로 비교함수를 사용하면 됩니다.
이 내용은 jakeSeo.velog를 많이 참조했습니다.
sort
함수의 기본 문법은arr.sort([compareFunction])
입니다. 여기서 우리가 정의해줘야 하는 것은[compareFunction]
부분입니다.
[compareFunction]
은sort
함수의콜백함수
로firstEl
과secondEl
이란 인자가 자동으로 들어갑니다. 이 콜백함수의 작성을 생략하면 이전에 설명했듯 문자열로 바뀐 뒤 UTF-16의 코드 유닛 값을 기준으로 정렬이 수행됩니다.
sort()
함수의 괄호 안에 2개의 변수를 사용하는 익명함수를 쓰면, 커스터마이징이 되어, 사용자가 원하는 소팅을 할 수 있다는 것이에요.
이 compareFunction
은 다음과 같은 특징이 있다고 합니다.
[compareFunction]
을 작성할 때는 어떤 값을 반환하는지가 중요 합니다. 숫자 값을 반환해야 하는데 총 3가지 경우로 나눌 수 있습니다.
0을 기준으로 3가지 케이스로 나뉩니다.
매개변수로
a, b
를 받았고 반환 값이 0보다 큰 경우에는 만일[a, b]
의 값이 들어왔다면, 그대로[a, b]
가 됩니다.a
가 먼저옵니다.
매개변수로a, b
를 받았고 반환 값이 0인 경우에는 만일[a, b]
의 값이 들어왔다면, 그대로[a, b]
가 됩니다.a
와b
의 위치를 그대로 둡니다.
매개변수로a, b
를 받았고 반환 값이 0보다 작은 경우에[a, b]
의 값이 들어왔다면,b
가 먼저 오게 됩니다.[b, a]
가 되는 겁니다.
즉, 음수가 리턴되면 순서를 바꾸어라!
그런데 말입니다. (김상중씨)
var numbers = [4, 2, 5, 1, 3];
numbers.sort(function(a, b) {
return a - b;
});
console.log(numbers);
// [1, 2, 3, 4, 5]
위와 같은 코드가 있으면, sort
에 있는 function
에 a
, b
가 차례로 들어가는데, 저는 처음에 당연히 첫 루프에 a
= 4, b
= 2 인 쌍으로 들어가는줄 알았습니다.
???? 근데 그게 아니래요! a
와 b
의 순서는 바뀌어 들어가서 판단이 된다고 하더라구요..?
즉, a
는 next
, b
는 previous
로 생각하면 된다고 .. 하더라구요. (왜? 진짜 1도 이해안감)
그렇다면 위의 코드를 다시 볼게요.
var numbers = [4, 2, 5, 1, 3];
numbers.sort(function(a, b) {
return a - b;
});
console.log(numbers);
// [1, 2, 3, 4, 5]
첫 루프를, 생각해봅시다. 첫 루프. 천천히.
a
는 next
이니까, 2
가 들어와요. 그리고,b
는, prev
니까 4
가 들어오네요.
a - b
값은, 그러면, 2 - 4
이니까, -2
네요!
앗 그러면 음수네요?! 그럼 바꿔야겠네요? [4, 2]
-> [2, 4]
...
그렇게, 쭉쭉 가다보면 결국엔 오름차순이 되겠죠 뭐 더 하나하나 해보고 싶지도 않아요..
--> 이 부분 관련해서 왜 순서가 바뀌어야하는지 아시는 분은 댓글.. 부탁드립니다 😭
sort 함수
의 핵심은 비교함수를 작성하는 것이고, 비교함수에서a(next)
와b(prev)
가 들어오고 무언가 바꾸고 싶을 때 -1을 리턴하는 것! 입니다.
이 정도가 맞는것 같아요.
많이 돌아왔네요..! 그래서 이번 소문제 문제는 연세가 많으신 순서대로 sort()
을 하는 문제였는데,
이거는 2개 방법으로 해보았어요.
const ordered = inventors.sort(function(a, b){
if(a.year > b.year){
return 1;
}
else{
return -1;
}
}
a
는 next
객체, b
는 previous
객체인거 생각하고, 정리하면,
만약 다음 객체의 year
가 크면, 즉 a
의 year
가 b
의 year
보다 크면,
그대로 순서를 냅두자! (if(a.year > b.year){ return 1 };
)
아니야! 이전 객체의 year
가 더 커! b
의 year
가 a
의 year
보다 크네?!
그럼 순서를 바꿔야 해! (else{ return -1; }
)
다른 방법으로는,
const sortInventors = inventors.sort((a, b) => a.year - b.year );
이렇게, 한줄로도 쓸 수가 있습니다. 이는 위에와 별 다른게 없을것 같아요.
a.year
- b.year
했는데(다음거 빼기 이전거), 양수 -> 그대로 / 음수 -> 바꿔
이정도로.. 설명이 가능할 것 같습니다. 😅
다음 질문입니다! 모든 사람의 수명을 더하라는, 누적시키는 기능을 물어보는 질문이였어욥.
reduce
메소드를 쓰라고 했는데, MDN
에 따르면, reduce
의 정의는 아래와 같습니다.
reduce()
메서드는 배열의 각 요소에 대해 주어진리듀서(reducer) 함수
를 실행하고, 하나의 결과값을 반환합니다.
기본형은 이렇게 생겼어요.
let value = arr.reduce(function(accumulator, item, index, array) { // ... }, [initial]);
accumulator
– 이전 함수 호출의 결과. initial은 함수 최초 호출 시 사용되는 초깃값을 나타냄(옵션)item
– 현재 배열 요소index
– 요소의 위치array
– 배열약간 복잡해 보이는데, 예를 들어볼게요.
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((sum, current) => sum + current, 0);
alert(result); // 15
여기서, sum
은, 누적한 값을 저장시키는 친구에요! 그 sum
이라는 변수는, 두번째 줄 맨 마지막인, 0
으로 초기화가 됩니다.
current
는 현재 배열 요소를 뜻합니다. forEach
에서 배열요소를 생각하면 될것 같아요.
자세한 로직 방향은 아래와 같습니다.
reduce
의 마지막 인수인 0(초깃값)
이 sum
에 할당됩니다. current
엔 배열의 첫 번째 요소인 1이 할당됩니다. 따라서 함수의 결과는 1이 됩니다.sum = 1
이고 여기에 배열의 두 번째 요소(2
)가 더해지므로 결과는 3
이 됩니다.sum = 3
이고 여기에 배열의 다음 요소가 더해집니다. 이런 과정이 계속 이어집니다.(출처: javascript.info)
const sum = inventors.reduce((acc, cur) => acc + (cur.passed - cur.year), 0);
이런식으로 만들어보았어요.
0
으로 초기화 시킨 acc
에 cur.passed
- cur.year
값을 더하면, 한 객체에 대한 수명이 계산되어, 이를 acc
에 누적시켰습니다 :)
글이 매우 길었네요!
배열의 기본적인 메소드들 filter
, map
, sort
, reduce
기능을 살펴보았습니다.
틀린내용이나 수정할 내용이 있다면 언제든지 피드백 부탁드립니다!
감사합니다!🤗