< 수정 전 >
- 조건 설명 -
두 배열이 있다.
그 안에 있는 요소들은 서로 겹치는 요소도 있고 아닌 요소도 있다.
동시에 두 배열은 null 이거나 비어 있을 수도 있고 아닐 수도 있다.
이 두 배열에 들어 있는 모든 요소들을 합해서 하나의 배열로 통합하려고 한다.
단, 모든 요소는 한 번만 들어가야 한다.
중복되는 요소가 있어서는 안 된다.
즉, 두 배열을 통합해서 새로운 배열을 만들고 싶다.
- 구상 -
처음에는 좀 어렵게 생각했다.
일단 두 배열을 합친다.
합친 배열 안에 있는 모든 요소들로부터 id 나 seq 처럼 unique 한 값만 추출해서 새로운 배열을 만든다.
이렇게 만든 id list 안에서 중복을 제거한다.
그러면 모든 요소들의 id 가 한 번씩만 들어가 있는 목록이 만들어진다.
그리고 이제 합쳐 놓았던 배열에서 요소들을 하나씩 꺼내어 검사한다.
요소로부터 id 값을 받아서 id list 에 그 id 가 있으면 최종 배열에 추가하고, 목록에서는 방금 그 id 를 지운다.
그러면 똑같은 요소가 다음에 한 번 더 들어왔을 때는 id 목록에 그 id 가 이미 지워진 상태이므로 최종 배열에 추가되지 않는다.
// 두 배열 합치기
let olderList = [];
olderList = olderList.concat(this.props.paramUserList || []);
olderList = olderList.concat(this.state.userList || []);
//id list 만들기
const userIdList = oldUserList.map((ele, idx, arr)=>{ return ele.userId});
// id list 안에서 중복 제거
const filteredUserIdSet = new Set(userIdList);
// 합친 배열에서 요소 하나를 꺼내어 해당 요소의 id가 id list 에 있다면 추가하고 없다면 넣지 않기
const newUserList = olderList.filter((ele, idx, arr)=>{
if(!filteredUserIdSet.has(ele.userId)) return false;
filteredUserIdSet.delete(ele.userId);
return true;
});
this.setState({...this.state, userList: newUserList});
- 코드 설명 -
/ null check
> let arrC = [];
> arrC.concat(arrA || []);
> arrC.concat(arrB || []);
> 위와 같은 구조로 짜 놓은 이유는 null check를 하기 위해서다.
> arrA.concat(arrB); 이렇게 해 놓으면 arrA 에 null 이 들어왔을 때 null.concat() 이 되므로 undefined 에러가 난다.
> 또 concat() 안에 파라미터로 null 이나 undefined 를 넣으면 그대로 null/undefined 가 배열 요소로 들어가 버린다.
> 즉, arr.concat(null); 이렇게 되면 arr = [null] 이 된다.
> 반면에 concat() 안에 빈 배열을 파라미터로 넣어주면 아무 요소도 추가되지 않는다.
> arr.concat([]); 이렇게 하면 arr = [] 그대로다.
> 따라서 concat 을 실행할 때 인스턴스와 파라미터를 모두 null check 해주기 위해서 먼저 새로운 배열을 그릇 삼아서 만들어 두고 그 그릇 배열에다가 concat 을 실행해 주면서 그 안에 들어가는 파라미터로 null 대신 빈 배열이 들어가도록 해 두었다.
/ 중복 제거
> const filteredUserIdSet = new Set(userIdList);
> 이 코드는 중복을 제거하기 위함이다.
> userIdList 안에 중복되는 값이 여러 개 존재하더라도 Set 은 중복을 허용하지 않는 자료형이기 때문에 List 를 재료로 Set 을 만들면 중복이 제거된다.
> 그러면 애초에 두 배열을 합쳐서 그 배열을 재료로 Set 을 만들면 중복이 제거되지 않겠는가?
> 뭐하러 복잡하게 id list 를 따로 추출하여 다시 Set 을 만드는가?
> Set 안에 숫자나 문자열 같은 기본 자료형이 들어 있으면 중복을 제거해줄 수 있지만, 객체인 경우에는 객체 안에 내용이 똑같더라도 메모리 참조값이 다르면 다른 요소로 판단하기 때문에 우리가 원하는 대로 작동하지 않는다.
< 수정 후 >
- 구상 -
find 함수를 활용하면 과정이 훨씬 간단해진다.
일단 두 배열을 합치는 단계까지는 똑같다.
그리고 그릇이 될 최종 배열을 새롭게 하나 따로 만들어 둔다.
이제 합친 배열로부터 요소를 하나씩 꺼내어 검사한다.
만약에 최종 배열 안에 들어 있지 않다면 해당 요소를 최종 배열에 넣어주고, 이미 있다면 넣지 않는다.
- find 함수 개념 정리 -
위 과정이 훨씬 간단하다.
처음부터 이렇게 구상하지 못했던 이유는 find 함수가 어떻게 쓰는 함수인지 확실히 개념이 안 잡혀 있었기 때문이다.
find 함수는 단순히 그 배열 안에 어떤 요소가 있는지 없는지 확인하는 배열인 줄 알았다.
하지만 실제로 find 함수는 좀 더 복잡한 기능을 수행할 수 있는 함수였다.
find 함수를 실행하면 배열로부터 요소들을 하나씩 꺼내와서 검사를 한다.
배열 안에서 요소들을 하나하나씩 꺼내어서 미리 짜 놓았던 로직을 실행해 보고 그 결과 return 값이 true 가 나오는 가장 처음 요소를 되돌려준다.
마지막 요소까지 true 가 나오지 않는다면 undefined 를 되돌려준다.
처음에는 find 함수가 단순히 똑같은 요소가 있는지 없는지 찾아주는 함수라고 생각했다.
파라미터로 어떤 요소를 넣고, 그 요소와 똑같은 요소가 배열 안에 이미 있는지 없는지 확인하는 정도라고 생각했다.
만약 그랬다면 자료형이 객체인 요소는 참조값 주소가 다르므로 당연히 제대로 찾아내지 못할 거라고 생각해서 처음부터 find 함수를 쓰지 않았다.
그러나 find 함수 안에 넣어주는 파라미터는 함수였다.
함수 안에서 배열이 던져주는 요소 하나를 파라미터로 받고, 그 요소를 가지고 이런 저런 로직을 실행해 볼 수 있었다.
그리고 return 값만 true 인지 false 인지 뱉어주면 됐다.
이렇게 하면 내가 원하는 판별식을 훨씬 유연하고 복잡하게 정해줄 수 있다.
그리고 그 조건을 만족하는 첫 번째 요소를 찾아서 되돌려주는 게 find() 함수의 기능이다.
참고로 어떤 조건을 만족하는 마지막 요소를 찾아서 반환해주는 함수도 있는데 arr.findLast() 이다.
// 두 배열 합치기
let oldUserList = [];
oldUserList = oldUserList.concat(this.props.paramUserList || []);
olderList = olderList.concat(this.state.userList || []);
// 그릇 배열을 만들어 두고 그 안에 있는지 확인한 뒤에 없다면 추가하기
const newUserList = [];
oldUserList.forEach((eleOld, idxOld, arrOld)=>{
const isExist = newUserList.find((eleNew, idxNew, arrNew)=>{ return eleNew.userId === eleOld.userId; });
if(isExist) return;
newUserList.push(eleOld);
})
this.setState({...this.state, userList: newUserList});
'JavaScript' 카테고리의 다른 글
[JavaScript] 값이 null 일 경우 parameter 에 key 조차 존재하면 안 될 때 (0) | 2023.01.09 |
---|---|
[JavaScript] concat() 함수 사용법 / 변이 (mutation) 함수 (0) | 2022.12.26 |
[JavaScript]두 배열 요소 합쳐서 return 하기 / concat 함수 null 체크 (0) | 2022.11.21 |
JavaScript ES6 문법 get/set 키워드 무한 루프 해결법 getter/setter 함수 사용법 (0) | 2022.11.14 |
[JavaScript] 걸려 있는 클릭 이벤트 핸들러 제거하기 remove Event Listener (0) | 2022.09.28 |