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

IT技术 javascript
2021-02-05 03:22:15

我的数组是这样的:

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

我想将其转换为:

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

所以,基本上,分组group

我想:

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

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

6个回答

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

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);

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

很好的答案。使用 Object.entries 和 ES6 我们也可以做到groups = Object.entries(group_to_values).map(([group, color]) => ({ group, color }));
2021-03-10 03:22:15
惊人的!我的问题是使用按键来形成一组对象Object.keys
2021-03-11 03:22:15
如果我的原始数组看起来像 {group: "one", color: "red", size:"big"},我将如何在最终对象中获取另一个元素?
2021-03-29 03:22:15

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

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

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]});
}

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

附录

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

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

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

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

然后像这样使用它:

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

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

@neuronaut 抱歉,但也许作者只是没有检查它并且除了数组中对象的格式之外没有任何要求。没关系,你的答案是肯定的,我不会试图评判你。但是如果您有时间纠正这个错误,那就太好了,这样其他人就可以使用您的代码而不是复制这个主题。谢谢!
2021-03-20 03:22:15
对不起,如果你有两个相同的对象,它不起作用_myArray_. With var myArray = [ {group: "one", color: "red"}, {group: "two", color: "blue"}, {group: "one", color: "green"}, {group: "one", color: "black"}, {group: "one", color: "black"} ]; 你会得到myArray[0].color = ["red", "green", "black", "black"]
2021-03-23 03:22:15
“不起作用”是主观的。OP 从未说明在这种情况下该怎么做。
2021-03-30 03:22:15
@Lends OP 从未将其指定为要求。事实上,鉴于 OP 的评论,我会说这个解决方案确实“有效”。
2021-04-06 03:22:15
发布的解决方案满足了要求,这个问题没有说明如何处理重复项。对未定义问题的“修复”甚至可能会让不期望该功能的未来读者更加困惑。
2021-04-09 03:22:15

使用lodash的groupby方法

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

所以使用 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>

结果不是所需的数据结构
2021-03-16 03:22:15

使用 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);

如果您的对象中除了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'));

如果您可以支持可选链接空合并运算符 (??),则可以将上述方法简化为以下内容:

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'));

一种选择是:

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/

@KingMob 定义“复杂”。这些是常规数组方法。
2021-03-10 03:22:15
非常优雅和自给自足,不错。我唯一的抱怨是它不像更简单的for循环那样立即可读
2021-03-16 03:22:15
reduce、indexOf、map、filter、map 对我来说似乎过于复杂。
2021-03-20 03:22:15
@NunoNogueira 不客气。这只是其中一种选择。
2021-03-25 03:22:15
它有效,但我需要一些时间来浏览代码并理解它。非常感谢你的帮助!
2021-04-05 03:22:15