JavaScript对象数组 - 去重并合并嵌套对象

3
仍在学习JS,我一直在解决一个问题,最近在Stack Overflow上找到了类似的解决方案link,我试图模仿这个解决方案,但是对于我的用例来说似乎无法实现...所以希望能就如何修复我编写的代码或指导正确方向提供一些帮助。
因此,我正在尝试根据“objectID”从对象数组(见下文)中删除重复项,并在每个级别(lvl0、lvl1、lvl2)合并嵌套的“hierarchicalCategories”,如果它们是唯一的则将其放入数组中。
let objArray = [
  {
  "objectID": "1234",
  "hierarchicalCategories": {
    "lvl0": "Women's"

  }
},
{
  "objectID": "1234",
  "hierarchicalCategories": {
    "lvl0": "Women's",
    "lvl1": "Women's > Jewelry",
    "lvl2": "Women's > Jewelry > New"
  }
},
 {
  "objectID": "1234",
  "hierarchicalCategories": {
    "lvl0": "New",
    "lvl1": "New > Jewelry"
  }
},
{
  "objectID": "5678",
  "hierarchicalCategories": {
    "lvl0": "Men's",
    "lvl1": "Men's > Shoes",

  }
},
{
  "objectID": "5678",
  "hierarchicalCategories": {
    "lvl0": "New",
    "lvl1": "New > Shoes"
  }
}
]


预期结果应该如下所示:每个“objectID”只有一个实例,如果每个层次都有唯一值,则合并“hierarchicalCategories”。
let newArray = [
 {
  "objectID": "1234",
  "hierarchicalCategories": {
    "lvl0": ["Women's","New"],
    "lvl1": ["Women's > Jewelry","New > Jewelry"],
    "lvl2": ["Women's > Jewelry > New"]
  }
},
{
  "objectID": "5678",
  "hierarchicalCategories": {
    "lvl0": ["Men's", "New"],
    "lvl1": ["Men's > Shoes","New > Shoes"]
  }
}
]

这是我使用的代码,它在一定程度上能够运行,但并不完全有效。基本上,在每个级别(lvl0、lvl1、lvl2)中,我都创建了一个数组,只有在之前未包含时才会将其推送进去。但是,如果没有定义级别,例如"objectID":"5678"并且在任何重复项中都没有定义"lvl2",那么在过滤后的数组中就会有一个空数组槽位,而我不希望出现这种情况,但似乎无法修复它而不完全破坏代码。也可以考虑其他建议和改进代码或其他方法。
const filteredArr = objArray.reduce((acc, current) => {
    const x = acc.find(item => item.objectID === current.objectID);
    if (!x) {

        current.hierarchicalCategories.lvl0 ? current.hierarchicalCategories.lvl0 = [current.hierarchicalCategories.lvl0] : current.hierarchicalCategories.lvl0 = []
        current.hierarchicalCategories.lvl1 ? current.hierarchicalCategories.lvl1 = [current.hierarchicalCategories.lvl1] : current.hierarchicalCategories.lvl1 = []
        current.hierarchicalCategories.lvl2 ? current.hierarchicalCategories.lvl2 = [current.hierarchicalCategories.lvl2] : current.hierarchicalCategories.lvl2 = []

        acc.push(current)

    } else {

        if (current.hierarchicalCategories.lvl0 && !x.hierarchicalCategories.lvl0.includes(current.hierarchicalCategories.lvl0)) {
            x.hierarchicalCategories.lvl0.push(current.hierarchicalCategories.lvl0)
        }
        if (current.hierarchicalCategories.lvl1 && !x.hierarchicalCategories.lvl1.includes(current.hierarchicalCategories.lvl1)) {
            x.hierarchicalCategories.lvl1.push(current.hierarchicalCategories.lvl1)
        }
        if (current.hierarchicalCategories.lvl2 && !x.hierarchicalCategories.lvl2.includes(current.hierarchicalCategories.lvl2)) {
            x.hierarchicalCategories.lvl2.push(current.hierarchicalCategories.lvl2)
        }

    }
    return acc;
}, []);

如您所见,我在响应中得到了空数组,位于第2级

[
 {
  "objectID": "1234",
  "hierarchicalCategories": {
    "lvl0": ["Women's","New"],
    "lvl1": ["Women's > Jewelry","New > Jewelry"],
    "lvl2": ["Women's > Jewelry > New"]
  }
},
{
  "objectID": "5678",
  "hierarchicalCategories": {
    "lvl0": ["Men's", "New"],
    "lvl1": ["Men's > Shoes","New > Shoes"],
    "lvl2": []
  }
}
]

感谢任何愿意提供帮助的人!

5个回答

6

有几种方法可以做到这一点,但我认为使用MapSet可以帮助对事物进行分组并使它们唯一:

let objArray = [{"objectID": "1234","hierarchicalCategories": {"lvl0": "Women's"}},{"objectID": "1234","hierarchicalCategories": {"lvl0": "Women's","lvl1": "Women's > Jewelry","lvl2": "Women's > Jewelry > New"}},{"objectID": "1234","hierarchicalCategories": {"lvl0": "New","lvl1": "New > Jewelry"}},{"objectID": "5678","hierarchicalCategories": {"lvl0": "Men's","lvl1": "Men's > Shoes",}},{"objectID": "5678","hierarchicalCategories": {"lvl0": "New","lvl1": "New > Shoes"}}];

let map = new Map(objArray.map(o => [o.objectID, {}] ));
for (let obj of objArray) {
    let cats = map.get(obj.objectID);
    for (let [key, val] of Object.entries(obj.hierarchicalCategories)) {
        cats[key] = (cats[key] || new Set).add(val);
    }
}
let result = Array.from(map.entries(), ([objectId, cats]) => ({ 
    objectId, 
    hierarchicalCategories: Object.fromEntries(Object.entries(cats).map(([k, v]) => 
        [k, [...v]]
    )) 
}));

console.log(result);

说明

首先创建一个以objectID为键,对应值初始化为空对象的Map。 Map构造函数得到一个[key, value]列表,然后从中创建Map。它不会抱怨提供给它的重复键。

然后对于输入数组中的每个对象,都检索并分配给cats相应的映射对象。第一次是空对象。然后将输入对象的hierarchicalCategories添加到cats中。在执行此操作时,验证是否在cats中已经存在键(如"lvl2")。如果不存在,则cats[key]未定义,然后||运算符仅评估右操作数,因此创建了一个Set。否则,我们知道它已经是一个Set。我们将该值(如"Woman's")添加到该集合中。使用Set的优点是会忽略重复项。

因此,这就是第一个for循环所做的。它基本上将输入转换为一种可以有效处理分组和重复项的结构。

然后代码的最后一部分将把此信息转换为所需的输出结构。

map.entries将提供它拥有的键/值组合。现在,值部分不再是对象,因为我们在之前的循环中向它们添加了数据。那些是可能有多个"lvl"键和关联Set的cats对象。

Array.from将允许我们迭代这些map.entries()并在映射器回调函数中对每个条目执行某些操作。该回调函数为每个条目返回一个对象。它用括号括起来,以避免JS解析器将大括号误解为代码块(实际上会抱怨它)。

使用Object.entries查找每个Set,并将其映射到标准数组,使用[...v]Object.fromEntries将其组合回一个对象(它是`Object.entries`的反向操作)。


这很棒,运行得很好,但您介意稍微详细解释一下它是如何工作的吗?我想尝试更多地了解一些,以便能够继续学习。 - mattylight10
@trincot 您的实现在我的编辑器中无法工作。 然而,要理解这个解决方案,我们需要了解新类型的对象 MapSet; 阅读文档 - Md. Amirozzaman
@MuhammadAmirozzamanNiaz,在2019年末,随着EcmaScript2019在去年6月发布,我们不能真正地争论MapSet是新的。它们自EcmaScript2015以来就已经存在了。这更表明你的编辑器没有跟上过去四年的发展。 - trincot
@trincot,第一次对任何人来说都是新的伙计。你说得对,这些不是新的。我想你理解我的观点。我的Node.js版本是10.16.3。 - Md. Amirozzaman

0

看起来是一个两步骤的过程:

  1. objectID 进行分组:

例如将 [{objectID: 1, ...}, {objectID: 1, ...}, {objectID: 2, ...}]

转换为 [ [{objectID: 1, ...}, {objectID: 1, ...}], [{objectID: 2, ...}] ]

  1. 对每个组进行 reduce (/deepmerge) 操作:

例如 [{objectID: 1, ...merged props}, {objectID: 2, ...merged props}]

this is an example 使用方便的 lodashdeepmerge 库中的 groupBy

deepmerge 是一个非常流行的库,用于合并具有/不具有数组/嵌套数组的复杂嵌套对象


我会查看一些那些库,谢谢你的见解。 - mattylight10

0

我喜欢简单。

const myArray = [{},{}];
const myArrayMirror = [...myArray];

const sortedArray = myArray.filter(originalObj, originalIndex => {
    let objIsDuplicate = false;
    myArrayMirror.forEach(duplicate, duplicateIndex => {
        if (duplicateIndex > originalIndex && originalObj === duplicate) {
            objIsDuplicate = true;
        }
    });
    return objIsDuplicate;
});

0
我会这样做:
const objArray = [YOUR_OBJECTS_LIVE_HERE]

const mapWithUniqueObjs = new Map()

// loop over properties to set it to your map
for (let i = 0; i < objArray.length; i++) {
  // Map's keys are always unique, so in case
  // we already have this item in the map, it will be overwritten
  uniqueObj.set(objArray[i].objectID, objArray[i])
}

// and make it an array
const arrWithUniqueObjs = [...mapWithUniqueObjs]


0
将以下代码添加到您的代码末尾(在分配filteredArray的位置):
.map(function(x) {
    if (x.hierarchicalCategories.lvl2.length === 0) {
      delete x.hierarchicalCategories.lvl2
    }
    if (x.hierarchicalCategories.lvl1.length === 0) {
      delete x.hierarchicalCategories.lvl1
    }
     if (x.hierarchicalCategories.lvl0.length === 0) {
      delete x.hierarchicalCategories.lvl0
    }
    return x
})

let objArray = [
  {
  "objectID": "1234",
  "hierarchicalCategories": {
    "lvl0": "Women's"

  }
},
{
  "objectID": "1234",
  "hierarchicalCategories": {
    "lvl0": "Women's",
    "lvl1": "Women's > Jewelry",
    "lvl2": "Women's > Jewelry > New"
  }
},
 {
  "objectID": "1234",
  "hierarchicalCategories": {
    "lvl0": "New",
    "lvl1": "New > Jewelry"
  }
},
{
  "objectID": "5678",
  "hierarchicalCategories": {
    "lvl0": "Men's",
    "lvl1": "Men's > Shoes",

  }
},
{
  "objectID": "5678",
  "hierarchicalCategories": {
    "lvl0": "New",
    "lvl1": "New > Shoes"
  }
}
]

const filteredArr = objArray.reduce((acc, current) => {
    const x = acc.find(item => item.objectID === current.objectID);
    if (!x) {

        current.hierarchicalCategories.lvl0 ? current.hierarchicalCategories.lvl0 = [current.hierarchicalCategories.lvl0] : current.hierarchicalCategories.lvl0 = []
        current.hierarchicalCategories.lvl1 ? current.hierarchicalCategories.lvl1 = [current.hierarchicalCategories.lvl1] : current.hierarchicalCategories.lvl1 = []
        current.hierarchicalCategories.lvl2 ? current.hierarchicalCategories.lvl2 = [current.hierarchicalCategories.lvl2] : current.hierarchicalCategories.lvl2 = []

        acc.push(current)

    } else {

        if (current.hierarchicalCategories.lvl0 && !x.hierarchicalCategories.lvl0.includes(current.hierarchicalCategories.lvl0)) {
            x.hierarchicalCategories.lvl0.push(current.hierarchicalCategories.lvl0)
        }
        if (current.hierarchicalCategories.lvl1 && !x.hierarchicalCategories.lvl1.includes(current.hierarchicalCategories.lvl1)) {
            x.hierarchicalCategories.lvl1.push(current.hierarchicalCategories.lvl1)
        }
        if (current.hierarchicalCategories.lvl2 && !x.hierarchicalCategories.lvl2.includes(current.hierarchicalCategories.lvl2)) {
            x.hierarchicalCategories.lvl2.push(current.hierarchicalCategories.lvl2)
        }

    }
    return acc;
}, []).map(function(x) {
    if (x.hierarchicalCategories.lvl2.length === 0) {
      delete x.hierarchicalCategories.lvl2
    }
    if (x.hierarchicalCategories.lvl1.length === 0) {
      delete x.hierarchicalCategories.lvl1
    }
     if (x.hierarchicalCategories.lvl0.length === 0) {
      delete x.hierarchicalCategories.lvl0
    }
    return x
})
console.log(filteredArr);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接