jgozal
jgozal

Reputation: 1582

Return an Array of Arrays containing objects that share a common value in a property

Say I have an array of 3 objects like this:

[
  {
    a: 4, 
    b: 5,
    c: 4
  },
  {
    a: 3, 
    b: 5,
    c: 6
  },
  {
    a: 2, 
    b: 3,
    c: 3
  }
]

I would like to return an array of arrays containing the objects that share a common value for the property b. So the resulting array would contain only one array containing 2 objects like this:

[

  [  
    {
      a: 4, 
      b: 5,
      c: 4
    },
    {
      a: 3, 
      b: 5,
      c: 6
    }
  ]

]

How would I do this?

Upvotes: 2

Views: 89

Answers (4)

Nenad Vracar
Nenad Vracar

Reputation: 122047

You could do this with map and filter

var data = [{"a":4,"b":5,"c":4},{"a":3,"b":5,"c":6},{"a":2,"b":3,"c":3}];

var check = data.map(e => {return e.b});
var result = [data.filter(e => { return check.indexOf(e.b) != check.lastIndexOf(e.b)})];

console.log(result)

To group multiple objects in separate arrays with same b values you can use map and forEach

var data = [{"a":4,"b":5,"c":4},{"a":3,"b":5,"c":6},{"a":2,"b":3,"c":3}, {"a":3,"b":7,"c":6},{"a":2,"b":7,"c":3}], result = [];

var check = data.map(e => {return e.b});
data.forEach(function(e) { 
 if(check.indexOf(e.b) != check.lastIndexOf(e.b) && !this[e.b]) {
   this[e.b] = [];
    result.push(this[e.b]);
 }
 (this[e.b] || []).push(e);
}, {});

console.log(result)

Upvotes: 3

MinusFour
MinusFour

Reputation: 14423

You could use a Map to group them, this should work with any kind of value (just be sure the equality rules check out):

var arr = [{
  a: 4,
  b: 5,
  c: 4

}, {
  a: 3,
  b: 5,
  c: 6
}, {
  a: 2,
  b: 3,
  c: 3
}];

var result = arr.reduce(function(m, o){
  var value = o.b;
  if(m.has(value)){
    m.get(value).push(o);
  } else {
    m.set(value, [o]);
  }
  return m;
}, new Map());

console.log(...(result.values()));

If you'd need to filter out the groups of 1:

var arr = [{
  a: 4,
  b: 5,
  c: 4

}, {
  a: 3,
  b: 5,
  c: 6
}, {
  a: 2,
  b: 3,
  c: 3
}];

var result = arr.reduce(function(m, o){
  var value = o.b;
  if(m.has(value)){
    m.get(value).push(o);
  } else {
    m.set(value, [o]);
  }
  return m;
}, new Map());

result = [...result.values()].filter(a => a.length > 1);
console.log(result);

Upvotes: 1

Himmel
Himmel

Reputation: 3709

You can create a function that accepts fulfillment criteria and will return as many nested arrays as rules passed.


Let's say you have an array of objects, arr.

var arr = [{a: 1, b: 2}, {a: 3, b: 2}, {a: 3, b: 4}, {a: 1, b: 1}]

And you want to return an array with with nested arrays that fulfill a particular requirement, let's say you want objects with an a:1 and b:2.

You can create a function that loops through your rules and creates a nested array with the objects that fulfill each rule.

For example:

var arr = [{a: 1, b: 2}, {a: 3, b: 2}, {a: 3, b: 4}, {a: 1, b: 1}]
    
function makeNestedArrays() {
  var rules = [].slice.call(arguments);
    
  return rules.reduce(function(acc, fn) {
    var nestedArr = [];

    arr.forEach(function(obj) {
      if (fn(obj)) {
        nestedArr.push(obj);
      }
    });
    
    // only push nested array
    // if there are matches
    if (nestedArr.length) {
      acc.push(nestedArr);
    }
        
    return acc;
  }, []);
}
    
var result = makeNestedArrays(
  function(obj) { return obj.a === 1; },
  function(obj) { return obj.b === 2; }
);

console.log(result);

This allows you to pass as many "rules" as you want, and will create a nested array for each rule so long as there is at least one match.

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386634

This proposal uses a single loop with Array#forEach but without Array#indexOf.

var array = [{ a: 4, b: 5, c: 4 }, { a: 3, b: 5, c: 6 }, { a: 2, b: 3, c: 3 }],
    grouped = [];

array.forEach(function (a) {
    this[a.b] = this[a.b] || [];
    this[a.b].push(a);
    this[a.b].length === 2 && grouped.push(this[a.b]);
}, Object.create(null));

console.log(grouped);

Upvotes: 2

Related Questions