Group array items using object

时间:2015-07-28 22:50:30

标签: javascript

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.

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.

18 个答案:

答案 0 :(得分:68)

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

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之类的数组实例方法为您提供了强大的高级构造,可以为您省去手动循环的痛苦。

答案 1 :(得分:26)

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.

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 groups object here helps speed things up because it allows you to avoid nesting loops to search through the arrays. Also, because groups is an object (rather than an array) iterating over it using for ... in is appropriate.

Addendum

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

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:

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

答案 2 :(得分:7)

使用lodash&#39; groupby方法

  

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

因此,使用lodash,您可以在一行中获得所需内容。见下文

&#13;
&#13;
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)
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
&#13;
&#13;
&#13;

答案 3 :(得分:5)

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/

答案 4 :(得分:4)

使用ES6,可以将dataTask(...){...}.resume()Map作为累加器,然后使用Array.from()及其映射功能将每个分组的map-entry映射到对象:

.reduce()

如果您的对象中除了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);group之外还具有其他属性,则可以通过将分组对象设置为地图的值来采用更通用的方法,如下所示:

color

如果可以支持optional chainingnullish coalescing operator (??),则可以将上述方法简化为以下内容:

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

答案 5 :(得分:3)

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

&#13;
&#13;
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);
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;

答案 6 :(得分:3)

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

答案 7 :(得分:2)

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.

答案 8 :(得分:1)

另一个选择是使用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);

答案 9 :(得分:1)

此仓库提供lodash解决方案和本机Js中的替代方案,您可以找到如何实现groupby。 https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function#Step_examples

答案 10 :(得分:1)

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

答案 11 :(得分:0)

使用数组的reducefindIndex方法,可以实现这一目标。

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

var transformedArray = myArray.reduce((acc, arr) => {
  var index = acc.findIndex(function(element) {
    return element.group === arr.group;
  });
  if (index === -1) {
    return acc.push({
      group: arr.group,
      color: [arr.color]
    });
  }
  
  acc[index].color.push(arr.color);
  return acc;
}, []);

console.log(transformedArray);
&#13;
&#13;
&#13;

通过使用reduce函数,数组是迭代器,新值存储在acc (accumulating)参数中。要检查给定group的对象是否已存在,我们可以使用findIndex函数。

如果findIndex()返回-1,则该值不存在,因此请在acc参数中添加该数组。

如果findIndex()返回索引,则使用index值更新arr

答案 12 :(得分:0)

您可以使用下一个扩展数组功能:

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

用法示例:

/* re-usable function */
Array.prototype.groupBy = function(prop) {
  var result = this.reduce(function (groups, item) {
      const val = item[prop];
      groups[val] = groups[val] || [];
      groups[val].push(item);
      return groups;
  }, {});
  return Object.keys(result).map(function(key) {
      return result[key];
  });
};

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

console.log(myArray.groupBy('group'));

积分:@Wahinya Brian

答案 13 :(得分:0)

我使用减速器的方法:

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

console.log(myArray.reduce( (acc, curr) => {
  const itemExists = acc.find(item => curr.group === item.group)
  if(itemExists){
    itemExists.color = [...itemExists.color, curr.color]
  }else{
    acc.push({group: curr.group, color: [curr.color]})
  }
  return acc;
}, []))

答案 14 :(得分:0)

如果您不想重复使用颜色值,这将为您提供独特的颜色

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

var arra = [...new Set(arr.map(x => x.group))]

let reformattedArray = arra.map(obj => {
   let rObj = {}
   rObj['color'] = [...new Set(arr.map(x => x.group == obj ? x.color:false ))]
       .filter(x => x != false)
   rObj['group'] = obj
   return rObj
})
console.log(reformattedArray)

答案 15 :(得分:0)

You can do something like this:

function convert(items) {
    var result = [];

    items.forEach(function (element) {
        var existingElement = result.filter(function (item) {
            return item.group === element.group;
        })[0];

        if (existingElement) {
            existingElement.color.push(element.color);
        } else {
            element.color = [element.color];
            result.push(element);
        }

    });

    return result;
}

答案 16 :(得分:0)

我喜欢使用 Map 构造函数回调来创建组(映射键)。第二步是填充该地图的值,最后以所需的输出格式提取地图数据:

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

let map = new Map(myArray.map(({group}) => [group, { group, color: [] }]));
for (let {group, color} of myArray) map.get(group).color.push(color);
let result = [...map.values()];

console.log(result);

 

答案 17 :(得分:-1)

尝试(h = {})

myArray.forEach(x=> h[x.group]= (h[x.group]||[]).concat(x.color) );
myArray = Object.keys(h).map(k=> ({group:k, color:h[k]}))

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

let h={};

myArray.forEach(x=> h[x.group]= (h[x.group]||[]).concat(x.color) );
myArray = Object.keys(h).map(k=> ({group:k, color:h[k]}))

console.log(myArray);