본문 바로가기
Web/JS

배열 내에 참조타입(객체 등)이 있을때 중복 제거하기

by 가나닩 2024. 10. 15.

자바스크립트 배열에서 중복을 제거하는 방법은 여러가지가 있지만 일반적으로는 Set 객체를 활용하면 편리하게 중복을 제거할 수 있다.

 

2024.10.01 - [기타/코딩테스트] - (JS) Set [Programmers - 신고 결과 받기]

 

(JS) Set [Programmers - 신고 결과 받기]

더보기코딩테스트 연습 - 신고 결과 받기 | 프로그래머스 스쿨 (programmers.co.kr) 프로그래머스코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하

cstelladev.tistory.com

 

하지만 배열 내에 참조 타입이 들어있으면 Set으로 중복 제거가 되지 않는다. 내용물이 같더라도 메모리 상에 서로 다른 데이터로 존재하며 그 위치를 참조하여 사용하기때문에 Set 객체에서 중복을 검사할때는 참조값이 달라 중복으로 처리되지 않는다.

 

참조 타입이 포함되어있는 배열에서 중복을 제거할때는 아래와 같은 방법을 사용할 수 있다.

 

 

1. JSON.stringify 활용

배열내에 있는 모든 항목을 문자열로 변환하여 참조 값이 아닌 실제 데이터를 직접 비교하는 방법이다.

const arr = [
  { name: "kim", age: 30 },
  { name: "kim", age: 40 },
  { name: "kim", age: 30 },
  { name: "park", age: 35 }
];

const result = Array.from(new Set(arr.map(item => JSON.stringify(item))))
	.map(item => JSON.parse(item));

console.log(result);
// [{ name: "kim", age: 30 }, { name: "kim", age: 40 }, { name: "park", age: 35 }]

 

배열내에 있는 모든 항목을 JSON 문자열로 변환하여 Set 객체로 중복을 제거한뒤 JSON.parse로 형식을 되돌린다. 이 방식을 활용하면 배열 항목으로 문자열만 들어있는 것과 같은 상황이므로 중복을 제대로 제거할 수 있다.

 

하지만 이 방식은 객체 혹은 배열내에 함수가 포함되어 있을 경우 사용할 수 없다. JSON.stringify는 함수를 직렬화하지 않기 때문이다. 함수가 포함된 배열에서 이 방식을 사용하면 JSON 표준에서 지원하지 않는 함수는 무시되어 빈 값이 되어버린다. 즉, 값 자체가 사라져 버린다.

const arr = [
  { a: 1, b: () => "hello" },
  { a: 1, b: () => "hello" },
  { a: 2, b: () => "world" }
];

const result = Array.from(new Set(arr.map(item => JSON.stringify(item))))
	.map(item => JSON.parse(item));

console.log(result);
// [{ "a": 1 }, { "a": 2 }]

 

JSON에서 지원되지 않는 함수가 제거되었다. 함수의 중복을 제거하려면 복잡한 로직이 필요할 수 있지만 filter를 활용하면 제한적인 조건에서 중복을 쉽게 제거할 수 있다.

 

 

2. filter 활용

배열내에 특정 항목을 지정하여 비교한뒤 중복을 제거하는 방식이다.

const arr = [
    { name: "kim", age: 30 },
    { name: "kim", age: 40 },
    { name: "kim", age: 30 },
    { name: "park", age: 35 },
];

const test = arr.filter((val, idx, self) =>
	idx === self.findIndex((t) => t.name === val.name));

console.log(test);
// [{ name: 'kim', age: 30 }, { name: 'park', age: 35 }]

 

배열내 객체에 있는 name이라는 key를 기준으로 잡고 비교하여 중복을 제거했다. 하지만 이 방식은 name이라는 key만을 기준으로 하므로 age의 차이를 구분할 수 없다. 이때에는 조건을 조금더 복잡하게 지정해야한다.

const arr = [
    { name: "kim", age: 30 },
    { name: "kim", age: 40 },
    { name: "kim", age: 30 },
    { name: "park", age: 35 },
];

const test = arr.filter((val, idx, self) =>
	idx === self.findIndex((t) => t.name === val.name && t.age === val.age));
    
console.log(test);
// [{ name: 'kim', age: 30 },{ name: 'kim', age: 40 },{ name: 'park', age: 35 }]

배열속 객체의 name과 age를 둘다 비교하여 중복이 정상적으로 제거되었다.

 

filter를 활용하면 복잡한 객체나 배열, 서버로부터 받은 다량의 데이터 등을 여러가지 기준으로 비교하여 처리할 수 있어 활용성이 높다.

 

JSON.stringify 방식에서 문제가 된 함수 중복처리도 다른 key값을 기준으로 하여 정상적인 제거가 일부 환경에서 가능하다 

const arr = [
  { a: 1, b: () => "hello" },
  { a: 1, b: () => "hello" },
  { a: 2, b: () => "world" }
];

const result = arr.filter((item, index, self) =>
  index === self.findIndex((t) => t.a === item.a)
);

console.log(result);
// [{ a: 1, b: () => "hello" },{ a: 2, b: () => "world" }]