Computer >> 컴퓨터 >  >> 프로그래밍 >> JavaScript

JavaScript 배열 그룹화 완벽 가이드: 지역별 데이터 묶기와 고유 사용자 수 계산하기

문제 소개

JavaScript로 데이터를 다루다 보면 객체 배열을 특정 기준으로 그룹화해야 하는 경우가 자주 발생합니다. 이번 글에서는 region(지역) 속성을 기준으로 객체 배열을 그룹화하고, 동시에 각 지역별 고유 사용자 수를 집계하는 방법을 알아보겠습니다.

먼저 다음과 같은 객체 배열이 있다고 가정해 보겠습니다.

const arr = [
    { region: "Africa", fruit: "Orange", user: "Gary" },
    { region: "Africa", fruit: "Apple", user: "Steve" },
    { region: "Europe", fruit: "Orange", user: "John" },
    { region: "Europe", fruit: "Apple", user: "bob" },
    { region: "Asia", fruit: "Orange", user: "Ian" },
    { region: "Asia", fruit: "Apple", user: "Angelo" },
    { region: "Africa", fruit: "Orange", user: "Gary" }
];

요구 사항

위 배열을 입력으로 받아 다음 작업을 수행하는 JavaScript 함수를 작성해야 합니다.

  • 객체의 region 속성을 기준으로 데이터를 그룹화한 새로운 배열을 생성합니다.
  • 각 지역별로 중복되지 않는 고유 사용자의 수를 함께 계산합니다.

예를 들어 위 배열에서 'Africa'에는 Gary와 Steve 두 명의 고유 사용자가 있으므로 count는 2가 됩니다. 마지막 요소의 Gary는 중복이므로 다시 세지 않습니다.

따라서 최종 출력 결과는 다음과 같은 형태가 되어야 합니다.

const output = [
    {
        "region": "Africa",
        "count": 2
    },
    {
        "region": "Europe",
        "count": 2
    },
    {
        "region": "Asia",
        "count": 2
    }
];

해결 코드

이 문제의 핵심은 이미 확인한 지역과 사용자 조합을 추적하는 것입니다. 아래 코드에서는 프로토타입 오염을 방지하기 위해 Object.create(null)로 생성한 순수 객체를 조회 테이블(lookup table)로 활용합니다.

const arr = [
    { region: "Africa", fruit: "Orange", user: "Gary" },
    { region: "Africa", fruit: "Apple", user: "Steve" },
    { region: "Europe", fruit: "Orange", user: "John" },
    { region: "Europe", fruit: "Apple", user: "bob" },
    { region: "Asia", fruit: "Orange", user: "Ian" },
    { region: "Asia", fruit: "Apple", user: "Angelo" },
    { region: "Africa", fruit: "Orange", user: "Gary" }
];

const groupByArea = (arr = []) => {
    const res = [];
    // 지역 및 지역+사용자 조합을 추적하기 위한 조회 테이블
    const seen = Object.create(null);

    arr.forEach(el => {
        // 해당 지역을 처음 만나면 결과 배열에 추가
        if (!seen[el.region]) {
            seen[el.region] = { region: el.region, count: 0 };
            res.push(seen[el.region]);
        }
        // 지역과 사용자를 조합한 고유 키 생성
        const key = [el.region, el.user].join('|');
        // 처음 보는 조합이라면 고유 사용자로 간주하고 카운트 증가
        if (!seen[key]) {
            seen[key] = true;
            seen[el.region].count++;
        }
    });

    return res;
};

console.log(groupByArea(arr));

코드 동작 원리

  1. 조회 테이블 초기화: Object.create(null)로 생성한 seen 객체는 프로토타입 체인이 없어 안전하게 키를 저장할 수 있습니다.
  2. 지역 그룹 생성: 배열을 순회하면서 처음 등장하는 지역이면 { region, count: 0 } 객체를 만들어 결과 배열에 추가합니다.
  3. 고유 키 생성: 지역명과 사용자명을 | 구분자로 연결하여 고유 키를 만듭니다. 이렇게 하면 같은 이름의 사용자가 다른 지역에 있어도 정확히 구분됩니다.
  4. 카운트 증가: 해당 키가 처음 등장하는 경우에만 카운트를 증가시키므로, 중복된 사용자(Gary의 두 번째 등장)는 자동으로 제외됩니다.

실행 결과

콘솔에 출력되는 결과는 다음과 같습니다.

[
    { region: 'Africa', count: 2 },
    { region: 'Europe', count: 2 },
    { region: 'Asia', count: 2 }
]

마무리

이처럼 조회 테이블 하나만 잘 활용하면 복잡해 보이는 그룹화·집계 문제도 O(n) 시간 복잡도로 깔끔하게 해결할 수 있습니다. 실무에서는 ES2024에 도입된 Object.groupBy()Map.groupBy() 메서드를 활용하면 더욱 간결하게 그룹화 로직을 작성할 수 있으니 함께 참고하시기 바랍니다.