将对象数组根据属性拆分为多个单独的数组

89

假设我有一个这样的数组:

var arr = [
    {type:"orange", title:"First"},
    {type:"orange", title:"Second"},
    {type:"banana", title:"Third"},
    {type:"banana", title:"Fourth"}
];

我希望将这个拆分成具有相同类型对象的数组,因此:

[{type:"orange", title:"First"},
{type:"orange", title:"Second"}]

[{type:"banana", title:"Third"},
{type:"banana", title:"Fourth"}]

但我希望通用化,不需要使用if语句来指定橙子或香蕉

// not like this
for (prop in arr){
    if (arr[prop] === "banana"){
       //add to new array
    }
}

你有什么想法?使用JQuery和Underscore都是可选项。


感谢您提供的非常有帮助的回复。我已经更改了等号。 - Evan
12个回答

84
这对Array.reduce(...)来说是一项简单的工作:
function groupBy(arr, property) {
  return arr.reduce(function(memo, x) {
    if (!memo[x[property]]) { memo[x[property]] = []; }
    memo[x[property]].push(x);
    return memo;
  }, {});
}

var o = groupBy(arr, 'type'); // => {orange:[...], banana:[...]}
o.orange; // => [{"type":"orange","title":"First"},{"type":"orange","title":"Second"}]
o.banana; // => [{"type":"banana","title":"Third"},{"type":"banana","title":"Fourth"}]

当然,如果你的目标浏览器不支持ECMAScript 262第五版,则必须自己实现“reduce”功能,或使用polyfill库,或选择另一个答案。

[更新] 这里有一个解决方案,适用于任何版本的JavaScript:

function groupBy2(xs, prop) {
  var grouped = {};
  for (var i=0; i<xs.length; i++) {
    var p = xs[i][prop];
    if (!grouped[p]) { grouped[p] = []; }
    grouped[p].push(xs[i]);
  }
  return grouped;
}

可能还需要包括一个兼容更多浏览器的版本。IE8-不支持“reduce”。 - Travis J
@TravisJ:是的,如果你的目标浏览器不支持EMCAScript 262第5版,那么你需要实现自己的“reduce”函数或选择其他答案。 - maerics

56

JQuery和Underscore都是可选的。

Underscore的groupBy正好符合您的需求。

_.groupBy(arr, "type")

16

ES6解决方案:

function groupBy(arr, property) {
  return arr.reduce((acc, cur) => {
    acc[cur[property]] = [...acc[cur[property]] || [], cur];
    return acc;
  }, {});
}

或完全实现 ES6:

const groupBy = (arr, property) => {
    return arr.reduce((acc, cur) => {
      acc[cur[property]] = [...acc[cur[property]] || [], cur];
      return acc;
    }, {});
}

希望它有所帮助!


1
仅供参考:调用此函数时,property 参数应为字符串。 - Kate Lupachova

14

这假设有一个对象数组:

function groupBy(array, property) {
    var hash = {};
    for (var i = 0; i < array.length; i++) {
        if (!hash[array[i][property]]) hash[array[i][property]] = [];
        hash[array[i][property]].push(array[i]);
    }
    return hash;
}

groupBy(arr,'type')  // Object {orange: Array[2], banana: Array[2]}
groupBy(arr,'title') // Object {First: Array[1], Second: Array[1], Third: Array[1], Fourth: Array[1]}

10

只需建立一个基于标题的对象字典。可以像这样完成:

js

var arr = [
{type:"orange", title:"First"},
 {type:"orange", title:"Second"},
 {type:"banana", title:"Third"},
 {type:"banana", title:"Fourth"}
];
var sorted = {};
for( var i = 0, max = arr.length; i < max ; i++ ){
 if( sorted[arr[i].type] == undefined ){
  sorted[arr[i].type] = [];
 }
 sorted[arr[i].type].push(arr[i]);
}
console.log(sorted["orange"]);
console.log(sorted["banana"]);

jsfiddle演示:http://jsfiddle.net/YJnM6/


非常好,还要赞一下称这个模式为字典! - Mark Carpenter Jr

7

Typescript 版本。

/**
* Group object array by property
 * Example, groupBy(array, ( x: Props ) => x.id );
 * @param array
 * @param property
 */
export const groupBy = <T>(array: Array<T>, property: (x: T) => string): { [key: string]: Array<T> } =>
  array.reduce((memo: { [key: string]: Array<T> }, x: T) => {
    if (!memo[property(x)]) {
      memo[property(x)] = [];
    }
    memo[property(x)].push(x);
    return memo;
  }, {});

export default groupBy;

1
现在这个功能已经由内置的Object.groupBy方法处理。

const arr = [
    {type:"orange", title:"First"},
    {type:"orange", title:"Second"},
    {type:"banana", title:"Third"},
    {type:"banana", title:"Fourth"}
];
const res = Object.groupBy(arr, o => o.type);
console.log(res);


在TypeScript中,你可以尝试像这样写:(Object as any).groupBy(arr, o => o.type); - undefined

1
例如,如果我们不想提到类型,那么我们可以像这样实现。

var arr = [
    {type:"orange", title:"First"},
    {type:"orange", title:"Second"},
    {type:"banana", title:"Third"},
    {type:"banana", title:"Fourth"}
];

const fun = (ar)=>{
  let temp ={}
for(e of ar){
  !temp[e.type] ? temp[e.type]=[e] : temp[e.type].push(e)
}
  return temp
}
console.log(fun(arr))


0

另一个typescript版本

最初受@denolsson的答案启发,但我删除了用作键的属性,因为它现在在内部数组中是多余的。

还更改和重命名了一些其他内容,以提高可读性并使linter满意)

/**
 * Inspired by https://dev59.com/bWUq5IYBdhLWcg3wNtuB#53632546
 */
private groupBy<T>(array: Array<T>, property: keyof T): { [key: string]: Array<T> } {
    return array.reduce(
        (objectToBeBuilt: { [key: string]: Array<T> }, arrayElem: T) => {
            
            const newOuterIdx = arrayElem[property] as unknown as string;
            
            if (!objectToBeBuilt[newOuterIdx]) {
                objectToBeBuilt[newOuterIdx] = [];
            }
            
            if (arrayElem[property]) {
                delete arrayElem[property];    
            }

            objectToBeBuilt[newOuterIdx]?.push(arrayElem);
            return objectToBeBuilt;
        },
        {}  // initial value of objectToBeBuild
    );
}

使用示例:

const resultGroupedByCustomer = this.groupBy(result.rows, "customer_id");

0

这是@denolssonTypeScript实现函数的改进:

/**
 * Group an array of objects by a specified property.
 * @param {Array<T>} array - The array of objects to group.
 * @param {string} property - The property to group the objects by.
 * @returns {Object} An object where the keys are the unique values of the specified property and the values are arrays of objects with that property value.
 * @template T
 *
 * @example
 * const arr = [{type:"A"}, {type:"A"}, {type:"B"}];
 * const result = groupBy(arr, "type");
 * console.log(result); // Output: { A: [{type: "A"}, {type: "A"}], B: [{type: "B"}] }
 */
export function groupBy<T>(array: Array<T>, property: string): { [key: string]: Array<T> } {
    return array.reduce((memo: { [key: string]: Array<T> }, x: T) => {
        memo[x[property]] ||= [];
        memo[x[property]].push(x);
        return memo;
    }, {});
}

以及 JavaScript 版本:

export function groupBy(array, property) {
    return array.reduce((memo, x) => {
        memo[x[property]] ||= [];
        memo[x[property]].push(x);
        return memo;
    }, {});
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接