Javascript 使用对象对数组项进行分组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31688459/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 07:02:37  来源:igfitidea点击:

Group array items using object

javascript

提问by Nuno Nogueira

My array is something like this:

我的数组是这样的:

myArray = [
  {group: "one", color: "red"},
  {group: "two", color: "blue"},
  {group: "one", color: "green"},
  {group: "one", color: "black"}
]

I want to convert this into:

我想将其转换为:

myArray = [
  {group: "one", color: ["red", "green", "black"]}
  {group: "two", color: ["blue"]}
]

So, basically, group by group.

所以,基本上,分组group

I'm trying:

我想:

for (i in myArray){
  var group = myArray[i].group;
  //myArray.push(group, {???})
}

I just don't know how to handle the grouping of similar group values.

我只是不知道如何处理相似组值的分组。

采纳答案by neuronaut

First, in JavaScript it's generally not a good idea to iterate over arrays using for ... in. See Why is using "for...in" with array iteration a bad idea?for details.

首先,在 JavaScript 中,使用for ... in. 请参阅为什么在数组迭代中使用“for...in”是个坏主意?详情。

So you might try something like this:

所以你可以尝试这样的事情:

var groups = {};
for (var i = 0; i < myArray.length; i++) {
  var groupName = myArray[i].group;
  if (!groups[groupName]) {
    groups[groupName] = [];
  }
  groups[groupName].push(myArray[i].color);
}
myArray = [];
for (var groupName in groups) {
  myArray.push({group: groupName, color: groups[groupName]});
}

Using the intermediary groupsobject here helps speed things up because it allows you to avoid nesting loops to search through the arrays. Also, because groupsis an object (rather than an array) iterating over it using for ... inis appropriate.

groups这里使用中间对象有助于加快速度,因为它允许您避免嵌套循环来搜索数组。此外,因为groups是一个对象(而不是数组)迭代它使用for ... in是合适的。

Addendum

附录

FWIW, if you want to avoid duplicate color entries in the resulting arrays you could add an ifstatement above the line groups[groupName].push(myArray[i].color);to guard against duplicates. Using jQuery it would look like this;

FWIW,如果您想避免在结果数组中出现重复的颜色条目,您可以if在该行上方添加一条语句groups[groupName].push(myArray[i].color);以防止重复。使用 jQuery 它看起来像这样;

if (!$.inArray(myArray[i].color, groups[groupName])) {
  groups[groupName].push(myArray[i].color);
}

Without jQuery you may want to add a function that does the same thing as jQuery's inArray:

如果没有 jQuery,您可能想要添加一个与 jQuery 执行相同操作的函数inArray

Array.prototype.contains = function(value) {
  for (var i = 0; i < this.length; i++) {
    if (this[i] === value)
      return true;
  }
  return false;
}

and then use it like this:

然后像这样使用它:

if (!groups[groupName].contains(myArray[i].color)) {
  groups[groupName].push(myArray[i].color);
}

Note that in either case you are going to slow things down a bit due to all the extra iteration, so if you don't need to avoid duplicate color entries in the result arrays I would recommend avoiding this extra code. There

请注意,在任何一种情况下,由于所有额外的迭代,您都会稍微减慢速度,因此如果您不需要避免结果数组中的重复颜色条目,我建议避免使用此额外代码。那里

回答by 1983

Start by creating a mapping of group names to values. Then transform into your desired format.

首先创建组名到值的映射。然后转换成你想要的格式。

var myArray = [
    {group: "one", color: "red"},
    {group: "two", color: "blue"},
    {group: "one", color: "green"},
    {group: "one", color: "black"}
];

var group_to_values = myArray.reduce(function (obj, item) {
    obj[item.group] = obj[item.group] || [];
    obj[item.group].push(item.color);
    return obj;
}, {});

var groups = Object.keys(group_to_values).map(function (key) {
    return {group: key, color: group_to_values[key]};
});

var pre = document.createElement("pre");
pre.innerHTML = "groups:\n\n" + JSON.stringify(groups, null, 4);
document.body.appendChild(pre);

Using Array instance methods such as reduceand mapgives you powerful higher-level constructs that can save you a lot of the pain of looping manually.

使用诸如reducemap 之类的Array 实例方法为您提供了强大的高级构造,可以为您省去很多手动循环的痛苦。

回答by undefined

One option is:

一种选择是:

var res = myArray.reduce(function(groups, currentValue) {
    if ( groups.indexOf(currentValue.group) === -1 ) {
      groups.push(currentValue.group);
    }
    return groups;
}, []).map(function(group) {
    return {
        group: group,
        color: myArray.filter(function(_el) {
          return _el.group === group;
        }).map(function(_el) { return _el.color; })
    }
});

http://jsfiddle.net/dvgwodxq/

http://jsfiddle.net/dvgwodxq/

回答by husayt

Use lodash's groupbymethod

使用lodash的groupby方法

Creates an object composed of keys generated from the results of running each element of collection thru iteratee. The order of grouped values is determined by the order they occur in collection. The corresponding value of each key is an array of elements responsible for generating the key. The iteratee is invoked with one argument: (value).

创建一个由通过迭代器运行集合的每个元素的结果生成的键组成的对象。分组值的顺序由它们在集合中出现的顺序决定。每个键对应的值是一个负责生成键的元素数组。迭代器使用一个参数调用:(值)。

So with lodash you can get what you want in a single line. See below

所以使用 lodash 你可以在一行中得到你想要的。见下文

let myArray = [
  {group: "one", color: "red"},
  {group: "two", color: "blue"},
  {group: "one", color: "green"},
  {group: "one", color: "black"},
]
let grouppedArray=_.groupBy(myArray,'group')
console.log(grouppedArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

回答by Nina Scholz

Beside the given approaches with a two pass approach, you could take a single loop approach by pushing the group if a new group is found.

除了使用两遍方法的给定方法之外,如果找到新组,您可以通过推送该组来采用单循环方法。

var array = [{ group: "one", color: "red" }, { group: "two", color: "blue" }, { group: "one", color: "green" }, { group: "one", color: "black" }],
    groups = Object.create(null),
    grouped = [];

array.forEach(function (o) {
    if (!groups[o.group]) {
        groups[o.group] = [];
        grouped.push({ group: o.group, color: groups[o.group] });
    }
    groups[o.group].push(o.color);
});

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

回答by GAURAV

myArray = [
  {group: "one", color: "red"},
  {group: "two", color: "blue"},
  {group: "one", color: "green"},
  {group: "one", color: "black"}
];


let group = myArray.map((item)=>  item.group ).filter((item, i, ar) => ar.indexOf(item) === i).sort((a, b)=> a - b).map(item=>{
    let new_list = myArray.filter(itm => itm.group == item).map(itm=>itm.color);
    return {group:item,color:new_list}
});
console.log(group);

回答by Eddie

Another option is using reduce()and new Map()to group the array. Use Spread syntaxto convert set object into an array.

另一种选择是使用reduce()new Map()对数组进行分组。使用Spread syntax转换集对象到一个数组。

var myArray = [{"group":"one","color":"red"},{"group":"two","color":"blue"},{"group":"one","color":"green"},{"group":"one","color":"black"}]

var result = [...myArray.reduce((c, {group,color}) => {
  if (!c.has(group)) c.set(group, {group,color: []});
  c.get(group).color.push(color);
  return c;
}, new Map()).values()];

console.log(result);

回答by Brandon

This version takes advantage that object keys are unique. We process the original array and collect the colors by group in a new object. Then create new objects from that group -> color array map.

此版本利用对象键是唯一的。我们处理原始数组并在新对象中按组收集颜色。然后从该组创建新对象 -> 颜色数组映射。

var myArray = [{
      group: "one",
      color: "red"
    }, {
      group: "two",
      color: "blue"
    }, {
      group: "one",
      color: "green"
    }, {
      group: "one",
      color: "black"
    }];

    //new object with keys as group and
    //color array as value
    var newArray = {};

    //iterate through each element of array
    myArray.forEach(function(val) {
      var curr = newArray[val.group]

      //if array key doesnt exist, init with empty array
      if (!curr) {
        newArray[val.group] = [];
      }

      //append color to this key
      newArray[val.group].push(val.color);
    });

    //remove elements from previous array
    myArray.length = 0;

    //replace elements with new objects made of
    //key value pairs from our created object
    for (var key in newArray) {
      myArray.push({
        'group': key,
        'color': newArray[key]
      });
    }

Please note that this does not take into account duplicate colors of the same group, so it is possible to have multiple of the same color in the array for a single group.

请注意,这不考虑同一组的重复颜色,因此单个组的数组中可能有多个相同颜色。

回答by om sinha

var array = [{
      id: "123",
      name: "aaaaaaaa"
    }, {
      id: "123",
      name: "aaaaaaaa"
    }, {
      id: '456',
      name: 'bbbbbbbbbb'
    }, {
      id: '789',
      name: 'ccccccccc'
    }, {
      id: '789',
      name: 'ccccccccc'
    }, {
      id: '098',
      name: 'dddddddddddd'
    }];
//if you want to group this array
group(array, key) {
  console.log(array);
  let finalArray = [];
  array.forEach(function(element) {
    var newArray = [];
    array.forEach(function(element1) {
      if (element[key] == element1[key]) {
          newArray.push(element)
      }
    });
    if (!(finalArray.some(arrVal => newArray[0][key] == arrVal[0][key]))) {
        finalArray.push(newArray);
    }
  });
  return finalArray
}
//and call this function
groupArray(arr, key) {
  console.log(this.group(arr, key))
}

回答by Nick Parsons

Using ES6, this can be done quite nicely using .reduce()with a Mapas the accumulator, and then using Array.from()with its mapping function to map each grouped map-entry to an object:

使用 ES6,这可以很好地使用.reduce()aMap作为累加器,然后使用Array.from()其映射函数将每个分组的映射条目映射到一个对象:

const arr = [{"group":"one","color":"red"},{"group":"two","color":"blue"},{"group":"one","color":"green"},{"group":"one","color":"black"}];

const res = Array.from(arr.reduce((m, {group, color}) => 
    m.set(group, [...(m.get(group) || []), color]), new Map
  ), ([group, color]) => ({group, color})
);

console.log(res);

If you have additional properties in your objects other than just groupand color, you can take a more general approach by setting a grouped object as the map's values like so:

如果您的对象中除了groupand之外还有其他属性color,您可以通过将分组对象设置为地图的值来采用更通用的方法,如下所示:

const arr = [{"group":"one","color":"red"},{"group":"two","color":"blue"},{"group":"one","color":"green"},{"group":"one","color":"black"}];

const groupAndMerge = (arr, groupBy, mergeInto) => 
  Array.from(arr.reduce((m, o) => {
    const curr = m.get(o[groupBy]);
    return m.set(o[groupBy], {...o, [mergeInto]: [...(curr && curr[mergeInto] || []), o[mergeInto]]});
  }, new Map).values());

console.log(groupAndMerge(arr, 'group', 'color'));

If you can support optional chaining, you can simplify the above method to the following:

如果可以支持optional chaining,则可以将上述方法简化为以下内容:

const arr = [{"group":"one","color":"red"},{"group":"two","color":"blue"},{"group":"one","color":"green"},{"group":"one","color":"black"}];
const groupAndMerge = (arr, groupBy, mergeWith) =>
  Array.from(arr.reduce((m, o) => m.set(o[groupBy], {...o, [mergeWith]: [...(m.get(o[groupBy])?.[mergeWith] || []), o[mergeWith]]}), new Map).values());

console.log(groupAndMerge(arr, 'group', 'color'));