Álvaro
Álvaro

Reputation: 2588

Add key to set of objects within array's set given match condition of object's key

I have this problem which I have tried to solve to no avail.

In this array of arrays, when two or more color keys in the objects match with any other set of array's objects color keys, I would like to add a boolean key called match to each of those array's objects, regardless of whether every key matches.

For example, two objects in the data[0][0] and data[0][1] arrays share the key color white and black, so that is a match: true for all objects in data[0][0] and data[0][1] but not data[0][2] as this only has one, therefore it will be match: false.

The result would look like this:

data = [
  [{
    name: 'car',
    color: 'black',
    group: 0,
    match: true
  },{
    name: 'car',
    color: 'white',
    group: 0,
    match: true
  },{
    name: 'car',
    color: 'blue',
    group: 0,
    match: true
  }],
  [{
    name: 'truck',
    color: 'black'
    group: 1,
    match: true
  },{
    name: 'truck',
    color: 'white',
    group: 1,
    match: true
  },{
    name: 'truck',
    color: 'yellow',
    group: 1,
    match: true
  }],
  [{
    name: 'moto',
    color: 'black',
    group: 2,
    match: false
  },{
    name: 'moto',
    color: 'pink',
    group: 2,
    match: false
  },{
    name: 'moto',
    color: 'orange',
    group: 2,
    match: false
  }]
]

This is a small sample. The actual data has hundreds of arrays and the match should be a minimum of 7

Upvotes: 0

Views: 64

Answers (2)

Patrick Roberts
Patrick Roberts

Reputation: 51886

You can calculate an intersection of colors as an array and compare the length to your expected threshold to determine the value of match in your results.

function match (data, key, filter, transform) {
  const arrays = data.map(
    array => array.map(key)
  );
  const groups = arrays.map(
    array => ({ array, set: new Set(array) })
  );
  const matches = groups.map(
    outer => groups.some(
      inner => (
        outer !== inner &&
        filter(outer.array.filter(inner.set.has, inner.set))
      )
    )
  );

  return data.map(
    (array, index) => transform(array, matches[index])
  );
}

const data = [[{name:'car',color:'black',group:0},{name:'car',color:'white',group:0},{name:'car',color:'blue',group:0}],[{name:'truck',color:'black',group:1},{name:'truck',color:'white',group:1},{name:'truck',color:'yellow',group:1}],[{name:'moto',color:'black',group:2},{name:'moto',color:'pink',group:2},{name:'moto',color:'orange',group:2}]];

const result = match(
  data,
  value => value.color,
  keys => keys.length >= 2,
  (array, match) => array.map(
    value => Object.assign(value, { match })
  )
);

console.log(result);

This creates a Set() for each array of colors to more efficiently calculate the intersection at each pass without skipping duplicates, if there are any.

For your actual data, you can change the filter parameter to

colors => colors.length >= 7

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386654

You could count black and white and get new objects with match.

var data = [[{ name: 'car', color: 'black', group: 0 }, { name: 'car', color: 'white', group: 0 }, { name: 'car', color: 'blue', group: 0 }], [{ name: 'truck', color: 'black', group: 1 }, { name: 'truck', color: 'white', group: 1 }, { name: 'truck', color: 'yellow', group: 1 }], [{ name: 'moto', color: 'black', group: 2 }, { name: 'moto', color: 'pink', group: 2 }, { name: 'moto', color: 'orange', group: 2 }]],
    result = data.map(a => {
        var count = {},
            match = a.some(({ color }) => {
                count[color] = (count[color] || 0) + 1;
                return count.black === 1 && count.white === 1;
            });
        return a.map(o => Object.assign({}, o, { match }));
    });

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

Upvotes: 0

Related Questions