Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loop over objects inside a list and return unique values

I'm looking for an efficient way to return unique values in objects inside an array. For example the next object:

{
    "products": [{
        "id": 1, 
        "category": "test1",
        "tags": {
            "option": ["A", "B"]
        }
    }, {
        "id": 2,
        "category": "test2",
        "tags": {
            "option": ["B"],
            "type": ["A", "B", "C"]
        }
    }, {
        "id": 3,
        "category": "test1",
        "tags": {
            "type": ["A"]
        }
    }, {
        "id": 4,
        "category": "test2",
        "tags": {
            "option": ["B", "C"],
            "type": ["A", "C"]
        }
    }]
}

What I want to return is the following:

{"option": [ "A", "B", "C" ] },{"type": ["A", "B", "C"] }

So I want for each item inside the tags object a new object. After that, I want an array with all unique values over all the products.

I do somewhat the same with another function:

Array.from(new Set(data.map(p => { return p.category; })))

This is a level higher which makes it easier. Can someone push me in the right direction?

like image 527
Frank Avatar asked Mar 04 '20 08:03

Frank


2 Answers

Make two sets instead, one for the options found so far, and one for the types found so far:

const obj = {
  "products": [{
    "id": 1,
    "tags": {
      "option": ["A", "B"]
    }
  }, {
    "id": 2,
    "tags": {
      "option": ["B"],
      "type": ["A", "B", "C"]
    }
  }, {
    "id": 3,
    "tags": {
      "type": ["A"]
    }
  }, {
    "id": 4,
    "tags": {
      "option": ["B", "C"],
      "type": ["A", "C"]
    }
  }]
};
const options = new Set();
const types = new Set();
for (const { tags: { option=[], type=[] } } of obj.products) {
  for (const o of option) options.add(o);
  for (const t of type) types.add(t);
}
console.log({
  option: [...options],
  type: [...types]
});

An alternative, for arbitrary keys:

const obj = {
  "products": [{
    "id": 1,
    "tags": {
      "option": ["A", "B"]
    }
  }, {
    "id": 2,
    "tags": {
      "option": ["B"],
      "type": ["A", "B", "C"]
    }
  }, {
    "id": 3,
    "tags": {
      "type": ["A"]
    }
  }, {
    "id": 4,
    "tags": {
      "option": ["B", "C"],
      "type": ["A", "C"]
    }
  }]
};
const setObj = {};
for (const { tags } of obj.products) {
  for (const [key, arr] of Object.entries(tags)) {
    if (!setObj[key]) setObj[key] = new Set();
    for (const item of arr) setObj[key].add(item);
  }
}
const output = Object.fromEntries(
  Object.entries(setObj).map(([key, set]) => [key, [...set]])
);
console.log(output);
like image 72
CertainPerformance Avatar answered Nov 12 '22 18:11

CertainPerformance


You could take a Map for wanted keys and collect the values in a Set.

function getUnique(array, keys) {
    var maps = new Map(keys.map(k => [k, new Set]));

    array.forEach(({ tags }) =>
        keys.forEach(k => (tags[k] || []).forEach(v => maps.get(k).add(v))));

    return Array.from(maps, ([k, s]) => ({ [k]: Array.from(s) }));
}

var data = { products: [{ id: 1, tags: { option: ["A", "B"] } }, { id: 2, tags: { option: ["B"], type: ["A", "B", "C"] } }, { id: 3, tags: { type: ["A"] } }, { id: 4, tags: { option: ["B", "C"], type: ["A", "C"] } }] },
    unique = getUnique(data.products, ['option', 'type']);

console.log(unique);
.as-console-wrapper { max-height: 100% !important; top: 0; }
like image 29
Nina Scholz Avatar answered Nov 12 '22 19:11

Nina Scholz