키로 객체 배열을 그룹화하는 방법


153

누구나 객체 키로 객체 배열을 그룹화 한 다음 그룹화를 기반으로 새 객체 배열을 만드는 방법을 알고 있습니까? 예를 들어, 자동차 객체 배열이 있습니다.

var cars = [
    {
        'make': 'audi',
        'model': 'r8',
        'year': '2012'
    }, {
        'make': 'audi',
        'model': 'rs5',
        'year': '2013'
    }, {
        'make': 'ford',
        'model': 'mustang',
        'year': '2012'
    }, {
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
    }, {
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
    },
];

다음과 같이 그룹화 된 새로운 자동차 객체 배열을 만들고 싶습니다 make.

var cars = {
    'audi': [
        {
            'model': 'r8',
            'year': '2012'
        }, {
            'model': 'rs5',
            'year': '2013'
        },
    ],

    'ford': [
        {
            'model': 'mustang',
            'year': '2012'
        }, {
            'model': 'fusion',
            'year': '2015'
        }
    ],

    'kia': [
        {
            'model': 'optima',
            'year': '2012'
        }
    ]
}

1
봤어 groupBy?
SLaks

2
결과가 유효하지 않습니다.
Nina Scholz

객체 대신 맵을 얻는 비슷한 방법이 있습니까?
Andrea Bergonzo

답변:


104

Timo의 대답 은 내가 어떻게 할 것인가입니다. 단순 _.groupBy하고 그룹화 된 구조의 오브젝트에서 일부 복제를 허용합니다.

그러나 OP는 중복 make키를 제거 하도록 요청했습니다 . 당신이 모든 길을 가고 싶다면 :

var grouped = _.mapValues(_.groupBy(cars, 'make'),
                          clist => clist.map(car => _.omit(car, 'make')));

console.log(grouped);

수율 :

{ audi:
   [ { model: 'r8', year: '2012' },
     { model: 'rs5', year: '2013' } ],
  ford:
   [ { model: 'mustang', year: '2012' },
     { model: 'fusion', year: '2015' } ],
  kia: [ { model: 'optima', year: '2012' } ] }

Underscore.js를 사용하여이 작업을 수행하려는 경우 해당 버전은 _.mapValues입니다 _.mapObject.


278

일반 자바 스크립트에서는 Array#reduce객체와 함께 사용할 수 있습니다

var cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }],
    result = cars.reduce(function (r, a) {
        r[a.make] = r[a.make] || [];
        r[a.make].push(a);
        return r;
    }, Object.create(null));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }


1
result결과를 어떻게 반복 할 수 있습니까?
Mounir Elfassi

1
Object.entries키 / 값 쌍으로 항목을 가져 와서 반복 할 수 있습니다 .
Nina Scholz

make그룹화 된 데이터 세트에서 그룹 을 제거하는 방법이 있습니까? 여분의 공간이 필요합니다.
Mercurial


R은 무엇을 의미합니까? r이 누산기이고 currentValue라고 가정하는 것이 맞습니까?
Omar

68

찾고 있습니다 _.groupBy().

필요한 경우 객체에서 그룹화하는 속성을 제거하는 것은 쉽지 않습니다.

var cars = [{'make':'audi','model':'r8','year':'2012'},{'make':'audi','model':'rs5','year':'2013'},{'make':'ford','model':'mustang','year':'2012'},{'make':'ford','model':'fusion','year':'2015'},{'make':'kia','model':'optima','year':'2012'},];

var grouped = _.groupBy(cars, function(car) {
  return car.make;
});

console.log(grouped);
<script src='https://cdn.jsdelivr.net/lodash/4.17.2/lodash.min.js'></script>


보너스로 ES6 화살표 기능을 사용하면 더 좋은 구문을 얻을 수 있습니다.

const grouped = _.groupBy(cars, car => car.make);

18
var grouped = _.groupBy(cars, 'make');접근자가 단순한 속성 이름이라면 , 더 짧은 시간을 원한다면 함수가 전혀 필요하지 않습니다.
Jonathan Eunice

1
'_'은 무엇을 의미합니까?
Adrian Grzywaczewski

@AdrianGrzywaczewski는 이름 간격 'lodash'또는 'underscore'의 기본 규칙이었습니다. 이제 라이브러리가 모듈 식이므로 더 이상 필요하지 않습니다. npmjs.com/package/lodash.groupby
vilsbole

5
그리고 결과에서 어떻게 개입 할 수 있습니까?
루이스 안토니오 페스타 나

36

es6에서 특정 키로 객체 배열을 그룹화하는 짧은 버전 :

result = array.reduce((h, obj) => Object.assign(h, { [obj.key]:( h[obj.key] || [] ).concat(obj) }), {})

더 긴 버전 :

result = array.reduce(function(h, obj) {
  h[obj.key] = (h[obj.key] || []).concat(obj);
  return h; 
}, {})

원래 질문은 자동차를 제조사별로 그룹화하는 방법을 묻는 것처럼 보이지만 각 그룹에서 제조업체를 생략합니다. 답은 다음과 같습니다.

result = cars.reduce((h, {model,year,make}) => {
  return Object.assign(h, { [make]:( h[make] || [] ).concat({model,year})})
}, {})

이것은 es5가 아닙니다
Shinigami

그냥 작동합니다!. 아무도이 축소 기능을 정교하게 할 수 있습니까?
Jeevan

두 답변이 마음에 들었지만 둘 다 "make"필드를 각 "make"배열의 멤버로 제공합니다. 제공된 출력이 예상 출력과 일치하는 위치를 기반으로 답변을 제공했습니다. 감사!
Daniel Vukasovich

15

https://github.com/you-dont-need/You-Dont-Need-Lodash-UnderscoregroupBy 의 코드를 일반화하는 고유 한 기능 은 다음과 같습니다 .

function groupBy(xs, f) {
  return xs.reduce((r, v, i, a, k = f(v)) => ((r[k] || (r[k] = [])).push(v), r), {});
}

const cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }];

const result = groupBy(cars, (c) => c.make);
console.log(result);


15

var cars = [{
  make: 'audi',
  model: 'r8',
  year: '2012'
}, {
  make: 'audi',
  model: 'rs5',
  year: '2013'
}, {
  make: 'ford',
  model: 'mustang',
  year: '2012'
}, {
  make: 'ford',
  model: 'fusion',
  year: '2015'
}, {
  make: 'kia',
  model: 'optima',
  year: '2012'
}].reduce((r, car) => {

  const {
    model,
    year,
    make
  } = car;

  r[make] = [...r[make] || [], {
    model,
    year
  }];

  return r;
}, {});

console.log(cars);


8

나는 떠날거야 REAL GROUP BY정확히 같은 JS 배열 예를 들어이 작업을 여기에

const inputArray = [ 
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
];

var outObject = inputArray.reduce(function(a, e) {
  // GROUP BY estimated key (estKey), well, may be a just plain key
  // a -- Accumulator result object
  // e -- sequentally checked Element, the Element that is tested just at this itaration

  // new grouping name may be calculated, but must be based on real value of real field
  let estKey = (e['Phase']); 

  (a[estKey] ? a[estKey] : (a[estKey] = null || [])).push(e);
  return a;
}, {});

console.log(outObject);


7

_.groupBy func에 의해 반복마다 호출되는 함수 내에서 객체를 수정하려고 할 수 있습니다. 소스 배열이 그의 요소를 변경한다는 것을 주목하십시오!

var res = _.groupBy(cars,(car)=>{
    const makeValue=car.make;
    delete car.make;
    return makeValue;
})
console.log(res);
console.log(cars);

1
이 코드는 문제를 해결하는 방법과 이유에 대한 설명포함 하여 문제를 해결할 수 있지만 게시물의 품질을 향상시키는 데 실제로 도움이됩니다. 지금 질문하는 사람뿐만 아니라 앞으로 독자들에게 질문에 대답하고 있음을 기억하십시오! 설명을 추가하기 위해 답을 편집하고 어떤 제한 및 가정이 적용되는지 표시하십시오.
Makyen

원하는 결과를 얻기 위해 배열을 한 번만 통과하기 때문에 나에게 가장 좋은 대답처럼 보입니다. make속성 을 제거하기 위해 다른 함수를 사용할 필요가 없으며 더 읽기 쉽습니다.
Carrm

7

간단한 for루프로 도 가능 합니다.

 const result = {};

 for(const {make, model, year} of cars) {
   if(!result[make]) result[make] = [];
   result[make].push({ model, year });
 }

그리고 아마도 더 빠르고 간단합니다. 입력하고 싶지 않은 db 테이블의 긴 필드 목록이 있으므로 스 니펫을 조금 더 동적으로 확장했습니다. 또한 const를 let으로 바꿔야합니다. for ( let { TABLE_NAME, ...fields } of source) { result[TABLE_NAME] = result[TABLE_NAME] || []; result[TABLE_NAME].push({ ...fields }); }
adrien


5

key가 null 일 수 있고 다른 것으로 그룹화하려는 경우

var cars = [{'make':'audi','model':'r8','year':'2012'},{'make':'audi','model':'rs5','year':'2013'},{'make':'ford','model':'mustang','year':'2012'},{'make':'ford','model':'fusion','year':'2015'},{'make':'kia','model':'optima','year':'2012'},
            {'make':'kia','model':'optima','year':'2033'},
            {'make':null,'model':'zen','year':'2012'},
            {'make':null,'model':'blue','year':'2017'},

           ];


 result = cars.reduce(function (r, a) {
        key = a.make || 'others';
        r[key] = r[key] || [];
        r[key].push(a);
        return r;
    }, Object.create(null));

4

재사용 할 수있는 방법을 만듭니다

Array.prototype.groupBy = function(prop) {
      return this.reduce(function(groups, item) {
        const val = item[prop]
        groups[val] = groups[val] || []
        groups[val].push(item)
        return groups
      }, {})
    };

그런 다음 아래의 기준으로 그룹화 할 수 있습니다

const groupByMake = cars.groupBy('make');
        console.log(groupByMake);

var cars = [
    {
        'make': 'audi',
        'model': 'r8',
        'year': '2012'
    }, {
        'make': 'audi',
        'model': 'rs5',
        'year': '2013'
    }, {
        'make': 'ford',
        'model': 'mustang',
        'year': '2012'
    }, {
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
    }, {
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
    },
];
  //re-usable method
Array.prototype.groupBy = function(prop) {
	  return this.reduce(function(groups, item) {
		const val = item[prop]
		groups[val] = groups[val] || []
		groups[val].push(item)
		return groups
	  }, {})
	};
  
 // initiate your groupBy. Notice the recordset Cars and the field Make....
  const groupByMake = cars.groupBy('make');
		console.log(groupByMake);
    
    //At this point we have objects. You can use Object.keys to return an array


3

ES6도 사용하는 프로토 타입 버전. 기본적으로 reduce 함수를 사용하여 누산기와 현재 항목을 전달한 다음 전달 된 키를 기반으로 "그룹화 된"배열을 빌드하는 데 사용합니다. Reduce의 내부 부분은 복잡해 보일 수 있지만 본질적으로 전달 된 객체의 키가 존재하는지 확인하고 비어있는 배열을 만들지 않으면 빈 항목을 만들고 현재 항목을 새로 만든 배열에 추가합니다. 연산자는 현재 키 배열의 모든 객체를 전달하고 현재 항목을 추가합니다. 이것이 누군가를 돕기를 바랍니다!.

Array.prototype.groupBy = function(k) {
  return this.reduce((acc, item) => ((acc[item[k]] = [...(acc[item[k]] || []), item]), acc),{});
};

const projs = [
  {
    project: "A",
    timeTake: 2,
    desc: "this is a description"
  },
  {
    project: "B",
    timeTake: 4,
    desc: "this is a description"
  },
  {
    project: "A",
    timeTake: 12,
    desc: "this is a description"
  },
  {
    project: "B",
    timeTake: 45,
    desc: "this is a description"
  }
];

console.log(projs.groupBy("project"));

1

다음 array#forEach()과 같은 방법 을 사용할 수도 있습니다 .

const cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }];

let newcars = {}

cars.forEach(car => {
  newcars[car.make] ? // check if that array exists or not in newcars object
    newcars[car.make].push({model: car.model, year: car.year})  // just push
   : (newcars[car.make] = [], newcars[car.make].push({model: car.model, year: car.year})) // create a new array and push
})

console.log(newcars);


1
function groupBy(data, property) {
  return data.reduce((acc, obj) => {
    const key = obj[property];
    if (!acc[key]) {
      acc[key] = [];
    }
    acc[key].push(obj);
    return acc;
  }, {});
}
groupBy(people, 'age');

1

이 방법으로 시도해보십시오.

let grouped = _.groupBy(cars, 'make');


2
잡히지 않은 ReferenceError : _이 (가) 정의되어 있지 않습니다. 솔루션을 해결하려면 타사 라이브러리를 설치해야합니다.
metakungfu

1
죄송합니다. 모두 알고 있습니다. _은 lodash lib에 주로 사용됩니다. lodash를 사용해야합니다. 질문을 읽고 lodash를 요청하고 있음을 알 수 있습니다. 고마워 나는 이것을 기억할 것이다. 그리고 lib를 쓰는 것을 잊지 마십시오.
agravat.in

1

외부 라이브러리를 사용하지 않는 각 솔루션의 성능을 테스트하기위한 벤치 마크를 만들었습니다.

JSBen.ch

reduce()@Nina 숄츠에 의해 게시 옵션은 최적의 하나가 될 것으로 보인다.


0

@metakunfu 답변이 마음에 들었지만 예상 출력을 정확하게 제공하지는 않습니다. 최종 JSON 페이로드에서 "make"를 제거하는 업데이트가 있습니다.

var cars = [
    {
        'make': 'audi',
        'model': 'r8',
        'year': '2012'
    }, {
        'make': 'audi',
        'model': 'rs5',
        'year': '2013'
    }, {
        'make': 'ford',
        'model': 'mustang',
        'year': '2012'
    }, {
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
    }, {
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
    },
];

result = cars.reduce((h, car) => Object.assign(h, { [car.make]:( h[car.make] || [] ).concat({model: car.model, year: car.year}) }), {})

console.log(JSON.stringify(result));

산출:

{  
   "audi":[  
      {  
         "model":"r8",
         "year":"2012"
      },
      {  
         "model":"rs5",
         "year":"2013"
      }
   ],
   "ford":[  
      {  
         "model":"mustang",
         "year":"2012"
      },
      {  
         "model":"fusion",
         "year":"2015"
      }
   ],
   "kia":[  
      {  
         "model":"optima",
         "year":"2012"
      }
   ]
}

0

lodash / fp를 사용하면 키로 _.flow()해당 첫 번째 그룹 의 함수를 작성한 다음 각 그룹을 맵핑하고 각 항목에서 키를 생략 할 수 있습니다.

const { flow, groupBy, mapValues, map, omit } = _;

const groupAndOmitBy = key => flow(
  groupBy(key),
  mapValues(map(omit(key)))
);

const cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }];

const groupAndOmitMake = groupAndOmitBy('make');

const result = groupAndOmitMake(cars);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>


0

모든 필드를 입력하지 않으려면 @Jonas_Wilms 의 답변 을 바탕으로하십시오.

    var result = {};

    for ( let { first_field, ...fields } of your_data ) 
    { 
       result[first_field] = result[first_field] || [];
       result[first_field].push({ ...fields }); 
    }

나는 벤치 마크를하지 않았지만 for 루프를 사용하는 것이이 답변 에서 제안 된 것보다 효율적이라고 생각 합니다.


0
const reGroup = (list, key) => {
    const newGroup = {};
    list.forEach(item => {
        const newItem = Object.assign({}, item);
        delete newItem[key];
        newGroup[item[key]] = newGroup[item[key]] || [];
        newGroup[item[key]].push(newItem);
    });
    return newGroup;
};
const animals = [
  {
    type: 'dog',
    breed: 'puddle'
  },
  {
    type: 'dog',
    breed: 'labradoodle'
  },
  {
    type: 'cat',
    breed: 'siamese'
  },
  {
    type: 'dog',
    breed: 'french bulldog'
  },
  {
    type: 'cat',
    breed: 'mud'
  }
];
console.log(reGroup(animals, 'type'));
const cars = [
  {
      'make': 'audi',
      'model': 'r8',
      'year': '2012'
  }, {
      'make': 'audi',
      'model': 'rs5',
      'year': '2013'
  }, {
      'make': 'ford',
      'model': 'mustang',
      'year': '2012'
  }, {
      'make': 'ford',
      'model': 'fusion',
      'year': '2015'
  }, {
      'make': 'kia',
      'model': 'optima',
      'year': '2012'
  },
];

console.log(reGroup(cars, 'make'));

0

다음과 같이 유형 스크립트로 그룹화 된 객체 배열 :

groupBy (list: any[], key: string): Map<string, Array<any>> {
    let map = new Map();
    list.map(val=> {
        if(!map.has(val[key])){
            map.set(val[key],list.filter(data => data[key] == val[key]));
        }
    });
    return map;
});

각 키를 검색 할 때 비효율적으로 보입니다. 검색은 O (n)의 복잡성을 가질 가능성이 높습니다.
Leukipp

0

나는 의존성 / 복잡성없이 순수한 간단한 js로 작성하는 것을 좋아합니다.

const mp = {}
const cars = [
  {
    model: 'Imaginary space craft SpaceX model',
    year: '2025'
  },
  {
    make: 'audi',
    model: 'r8',
    year: '2012'
  },
  {
    make: 'audi',
    model: 'rs5',
    year: '2013'
  },
  {
    make: 'ford',
    model: 'mustang',
    year: '2012'
  },
  {
    make: 'ford',
    model: 'fusion',
    year: '2015'
  },
  {
    make: 'kia',
    model: 'optima',
    year: '2012'
  }
]

cars.forEach(c => {
  if (!c.make) return // exit (maybe add them to a "no_make" category)

  if (!mp[c.make]) mp[c.make] = [{ model: c.model, year: c.year }]
  else mp[c.make].push({ model: c.model, year: c.year })
})

console.log(mp)


-1

여기에 또 다른 해결책이 있습니다. 요청대로.

make로 그룹화 된 새로운 자동차 객체 배열을 만들고 싶습니다.

function groupBy() {
  const key = 'make';
  return cars.reduce((acc, x) => ({
    ...acc,
    [x[key]]: (!acc[x[key]]) ? [{
      model: x.model,
      year: x.year
    }] : [...acc[x[key]], {
      model: x.model,
      year: x.year
    }]
  }), {})
}

산출:

console.log('Grouped by make key:',groupBy())

-1

다음은 Java의 Collectors.groupingBy ()에서 영감을 얻은 솔루션입니다.

function groupingBy(list, keyMapper) {
  return list.reduce((accummalatorMap, currentValue) => {
    const key = keyMapper(currentValue);
    if(!accummalatorMap.has(key)) {
      accummalatorMap.set(key, [currentValue]);
    } else {
      accummalatorMap.set(key, accummalatorMap.get(key).push(currentValue));
    }
    return accummalatorMap;
  }, new Map());
}

그러면 Map 객체가 제공됩니다.

// Usage

const carMakers = groupingBy(cars, car => car.make);

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.