문제 소개
여러 국가와 그 하위의 주(province), 그리고 각 주에 속한 도시 정보를 계층 구조로 담고 있는 JSON 객체가 있다고 가정해 보겠습니다. 아래 예시 데이터에는 Bangladesh와 India 두 나라의 도시 정보가 포함되어 있습니다.
const countryInfo = {
country: [{
name: "Bangladesh",
province: [{
name:"Dhaka",
city: [{
name:"Tangail",
lat: '11'
}, {
name:"Jamalpur",
lat: '12'
}]
}, {
name: "Khulna",
city: [{
name:"Jossore",
lat: '22'
}, {
name:"Tangail",
lat: '23'
}]
}, {
name: "Rajshahi",
city: [{
name:"Pabna",
lat: '33'
}, {
name:"Rangpur",
lat: '33'
}]
}]
},{
name: "India",
province: [{
name:"West Bengal",
city: [{
name:"Calcutta",
lat: '111'
}, {
name:"Tangail",
lat: '112'
}]
}, {
name:"Uttar Pradesh",
city: [{
name:"Agra",
lat: '122'
}, {
name:"Tajmahal",
lat: '123'
}]
}, {
name: "Rajasthan",
city: [{
name:"Kanpur",
lat: '131'
}, {
name:"Jaypur",
lat: '132'
}]
}]
}]
};
이제 이러한 객체와 검색 문자열을 인자로 받아, 검색어와 이름이 일치하는 모든 도시 객체를 배열로 반환하는 JavaScript 함수를 작성해 보겠습니다. 핵심은 같은 이름의 도시가 여러 나라, 여러 주에 중복해서 존재할 수 있다는 점입니다. 예를 들어 'Tangail'은 Bangladesh의 두 주와 India의 한 주에 걸쳐 등장하므로, 검색 시 세 곳 모두에서 찾아내야 합니다.
구현 코드
const searchForCity = (obj, query) => {
const cities = obj.country.reduce((acc, val) => {
val.province.forEach(el => {
el.city.forEach(elm => {
acc.push(elm);
});
});
return acc;
}, []);
const res = cities.filter(el => {
return el.name === query;
});
return res;
};
console.log(searchForCity(countryInfo, 'Tangail'));
코드 동작 원리
위 함수는 크게 두 단계로 동작합니다.
- 도시 목록 평탄화:
reduce()로 country 배열을 순회하면서, 각 국가의 province 배열과 그 안의 city 배열을 중첩forEach()로 끝까지 탐색해 모든 도시 객체를 하나의 배열(acc)에 모읍니다. - 조건에 맞는 도시 필터링: 평탄화된 배열에
filter()를 적용해 name 속성이 검색어와 정확히 일치(===)하는 도시만 남긴 뒤 그 결과를 반환합니다.
대소문자 구분 없이 부분 일치 검색을 원한다면 마지막 비교 조건을 el.name.toLowerCase().includes(query.toLowerCase())로 변경하면 됩니다.
실행 결과
'Tangail'로 검색했을 때 콘솔 출력은 다음과 같습니다.
[
{ name: 'Tangail', lat: '11' },
{ name: 'Tangail', lat: '23' },
{ name: 'Tangail', lat: '112' }
]
같은 이름의 도시가 서로 다른 위도(lat) 값을 가진 채 여러 곳에 존재하기 때문에 세 개의 객체가 모두 반환됩니다. 이처럼 reduce()로 깊이 중첩된 데이터를 평탄화한 뒤 filter()로 조건을 적용하는 패턴은, 복잡한 JSON 구조에서 원하는 데이터를 유연하게 검색할 때 널리 활용되는 방식입니다.