遍历列表内的对象并返回唯一值

时间:2020-03-04 08:43:54

标签: javascript arrays

我正在寻找一种有效的方法来在数组内的对象中返回唯一值。例如下一个对象:

{
    "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"]
        }
    }]
}

我想返回的是以下内容:

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

所以我要为标签对象内的每个项目创建一个新对象。之后,我想要一个在所有产品上都具有唯一值的数组。

我在另一个功能上做了一些相同的事情:

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

这是一个较高的级别,可以更轻松地进行。有人可以将我推向正确的方向吗?

5 个答案:

答案 0 :(得分:6)

改为设置两组,一组用于到目前为止找到的option,而另一组用于到目前为止找到的type

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

另一种选择,用于任意键:

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

答案 1 :(得分:3)

您可以使用Map作为通缉钥匙,并将值收集在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; }

答案 2 :(得分:3)

您可以使用两个必需的键创建一个对象,然后将其设置为空数组。然后遍历数组并将新元素添加到该数组中,然后删除重复项

const arr = [{
"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 object = {option:[] , type: []}
arr.forEach(({tags}) => {
  for(let prop in object){
    if(tags[prop]){
      object[prop] = [...new Set([...object[prop], ...tags[prop]])]
    }
  }
  
})
console.log(object)

答案 3 :(得分:3)

您可以使用Array.prototype.reduce()来使结果变量{ option: [], type: [] }

代码:

const 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"]}}]}

const result = data.products.reduce((a, { tags }) => {
  ['option', 'type'].forEach(prop => {
    a[prop] = [...new Set(a[prop].concat(tags[prop] || []))]
  })
  return a
}, { option: [], type: [] })

console.log(result)

答案 4 :(得分:3)

一个简单而有效的解决方案是:

const optionSet = new Set();
const typeSet = new Set();

data.products.forEach( pr =>{
    if(pr.tags.option){
      pr.tags.option.forEach( op =>{
        optionSet.add(op)
      })
    }
    if(pr.tags.type){
      pr.tags.type.forEach( tp =>{
        typeSet.add(tp);
      })
    }
})

性能比较:

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"]
    }
  }]
};

//efficient solution
let t0 = performance.now();
const optionSet = new Set();
const typeSet = new Set();

obj.products.forEach( pr =>{
    if(pr.tags.option){
      pr.tags.option.forEach( op =>{
        optionSet.add(op)
      })
    }
    if(pr.tags.type){
      pr.tags.type.forEach( tp =>{
        typeSet.add(tp);
      })
    }
})
let s1Result = { 
  options: [...optionSet],
  types: [...typeSet]
}
let t1 = performance.now();
let s1Runtime = t1-t0
console.log("efficient took: ",s1Runtime);


//accepted answer
let t2 = performance.now();
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 s2Result = Object.fromEntries(
  Object.entries(setObj).map(([key, set]) => [key, [...set]])
);
let t3 = performance.now();
let s2Runtime = t3-t2
console.log("current solution took: ",s2Runtime);


//runtime comparison
console.log("efficient solution is "+ ((s1Runtime)/(s2Runtime)).toFixed(2)*100 + " % faster current solution");

console.log("efficient solution result:", s1Result);
console.log("current solution result:", s2Result);