2

Suppose I have this array:

[
    {"type": "A",   "status": "CREATED",        "name": "Jack"}, 
    {"type": "A",   "status": "CREATED",        "name": "John"}, 
    {"type": "A",   "status": "UPDATED",        "name": "Alex"}, 
    {"type": "B",   "status": "UPDATED",        "name": "Jane"}
]

and I want to have a new array grouped by "type" and "status" and have the names in a list. This is the desired output:

[
    {"type": "A",   "status": "CREATED",        "name-list": ["Jack", "John"]}, 
    {"type": "A",   "status": "UPDATED",        "name-list": ["Alex"]}, 
    {"type": "B",   "status": "UPDATED",        "name-list": ["Jane"]}
]

as you can see the first object of the array contains 2 names because belong to the same "type" and "status". (name-list is just an example, could also remain name, the important thing is that it should be an array of strings/objects)

I have to represent these data in an html page and so needs to be able to cycle them via JavaScript.

Yevhen Horbunkov
  • 14,965
  • 3
  • 20
  • 42
andQlimax
  • 778
  • 10
  • 24

5 Answers5

3

You may traverse your source array with Array.prototype.reduce() building up the Map, having type and status combined as a key and respective object, with merged name as a value. Then extract Map.prototype.values() from that Map:

const src = [{"type":"A","status":"CREATED","name":"Jack"},{"type":"A","status":"CREATED","name":"John"},{"type":"A","status":"UPDATED","name":"Alex"},{"type":"B","status":"UPDATED","name":"Jane"}],

     result = [...src
        .reduce((r, o) => {
          const key = o.type+'\ud8ff'+o.status,
                match = r.get(key)
          match ? match.name.push(o.name) : r.set(key, {...o, name: [o.name]})
          return r
        }, new Map())
        .values()
      ]
      
console.log(result)
.as-console-wrapper{min-height:100%;}
Yevhen Horbunkov
  • 14,965
  • 3
  • 20
  • 42
  • Tha's pretty cool, how about if I have an additional integer parameter in each object, that I want to sum when grouping the names? – andQlimax Jul 22 '20 at 16:33
  • 1
    @andQlimax : that would be something, as simple as [that](https://codepen.io/ygorbunkov/pen/QWyYrvx?editors=0012) – Yevhen Horbunkov Jul 22 '20 at 16:48
2

I would try with .reduce() and .find():

const data = [
    {"type": "A",   "status": "CREATED",        "name": "Jack"}, 
    {"type": "A",   "status": "CREATED",        "name": "John"}, 
    {"type": "A",   "status": "UPDATED",        "name": "Alex"}, 
    {"type": "B",   "status": "UPDATED",        "name": "Jane"}
];

const result = data.reduce((a, { type, status, name}) => {
  const found = a.find(e => e.type === type && e.status === status);
  
  if (found) {
    found['name-list'].push(name);
  } else {
    a.push({
      type,
      status,
      ['name-list']: [name]
    });
  }
  
  return a;
}, []);

console.log(result);
norbitrial
  • 14,716
  • 7
  • 32
  • 59
1

const arr = [
    {"type": "A",   "status": "CREATED",        "name": "Jack"}, 
    {"type": "A",   "status": "CREATED",        "name": "John"}, 
    {"type": "A",   "status": "UPDATED",        "name": "Alex"}, 
    {"type": "B",   "status": "UPDATED",        "name": "Jane"}
]

const res = arr.reduce((acc, cur) => {
  
  acc.forEach((obj, idx) => {
      if(obj.type == cur.type && obj.status == cur.status){
          acc[idx]["name-list"].push(cur.name)
      }
  })
  
  if(!acc.some(obj => obj["name-list"].includes(cur.name))){
      acc.push({type: cur.type, status: cur.status, "name-list": [cur.name]})
  }
  
  return acc
},[])

console.log(res)
symlink
  • 11,984
  • 7
  • 29
  • 50
0

Using reduce and Object.values

let arr = [
    {"type": "A",   "status": "CREATED",        "name": "Jack"}, 
    {"type": "A",   "status": "CREATED",        "name": "John"}, 
    {"type": "A",   "status": "UPDATED",        "name": "Alex"}, 
    {"type": "B",   "status": "UPDATED",        "name": "Jane"}
];

arr = Object.values(
    arr.reduce((acc, cur) => {
        if(acc[`${cur.type}-${cur.status}`]) acc[`${cur.type}-${cur.status}`]["name-list"].push(cur.name)

        else {
            acc[`${cur.type}-${cur.status}`] = {type:cur.type, status:cur.status, "name-list":[cur.name]}
        }    
        return acc;
    }, {})
);

console.log(arr)
Ravi Kukreja
  • 627
  • 1
  • 8
  • 17
  • Using hyphen as delimiter is sort of risky as you, would get merged unexpectedly objects with `{type: 'a-b', status:'c..}` and `{type: 'a', status: 'b-c'..}. You should use something more safe as delimiter, you may refer to [my answer](https://stackoverflow.com/a/63037887/11299053) for example. – Yevhen Horbunkov Jul 22 '20 at 15:59
0

Here is what you want:

const ar = [
    { "type": "A", "status": "CREATED", "name": "Jack" },
    { "type": "A", "status": "CREATED", "name": "John" },
    { "type": "A", "status": "UPDATED", "name": "Alex" },
    { "type": "B", "status": "UPDATED", "name": "Jane" }
];
const res = Array.from(ar.reduce((a, c) => {
    if (a.has(c.type + c.status)) {
        a.get(c.type + c.status)['name-list'].push(c.name);
    } else {
        c['name-list'] = [c.name];
        delete c.name;
        a.set(c.type + c.status, c)
    }
    return a;
}, new Map()).values());

console.log(res);
8HoLoN
  • 1,122
  • 5
  • 14
  • Using `type` and `status` concatenated as a Map key is somewhat dangerous, as you may get merged unexpectedly objects with `{type: 'a', status: 'bc'..}` and `{type: 'ab', status: 'c'..}`. You'd be much better off using some *safe* character as delimiter. You may refer [my ansewr](https://stackoverflow.com/a/63037887/11299053) for example. – Yevhen Horbunkov Jul 22 '20 at 16:03