繁体   English   中英

通过比较数组内的对象数组来合并数组

[英]Merge an array by comparing the array of objects inside the array

我有以下数组

    var array = [
        {
            group: "FL",
            list: [
                { key: "A", value: "Alaska" },
                { key: "B", value: "Brazil" },
                { key: "C", value: "California" }
            ]
        },
        {
            group: "NZ",
            list: [
                { key: "A", value: "Alaska" },
                { key: "B", value: "Brazil" },
                { key: "D", value: "Delhi" }
            ]
        },
        {
            group: "QA",
            list: [
                { key: "A", value: "Alaska" },
                { key: "B", value: "Brazil" },
                { key: "C", value: "California" }
            ]
        }
    ]

我需要检查列表数组,如果列表数组中的所有对象都完全相同,那么我需要将其合并如下:

    [
        {
            group: "FL,QA",
            list: [
                { key: "A", value: "Alaska" },
                { key: "B", value: "Brazil" },
                { key: "C", value: "California" }
            ]
        },
        {
            group: "NZ",
            list: [
                { key: "A", value: "Alaska" },
                { key: "B", value: "Brazil" },
                { key: "D", value: "Delhi" }
            ]
        }
    ]

我通过使用reduce方法循环数组和其他两个函数来比较对象来尝试这个,但不知何故它不起作用

    array.reduce(async(acc, item) => {
        const exist = await compareObjects(acc, item);
        if (exist) {
            acc[exist.index].group= exist.group + ',' + item.group;
        } else {
            acc.push(item)
        }
      return acc;
    }, [])
    async function compareObjects(o1, o2) {
        for (let i = 0; i < o1.length; i++) {
           const value = await checkObjs(o1[i].list, o2.list);
            if(value) { return {index:i  , group: o1[i].group} }
        }
    }

    function checkObjs(arr1, arr2) {
        return arr1.length === arr2.length && arr1.every((el, i) => objectsEqual(el, arr2[i]))
    }

    const objectsEqual = (o1, o2) =>
        Object.keys(o1).length === Object.keys(o2).length
        && Object.keys(o1).every(p => o1[p] === o2[p]);

任何帮助,将不胜感激 。 谢谢

您可以使用Array.reduce()创建输入对象的映射。

我们将创建一个函数getListKey()来根据每个对象列表创建一个唯一的键。

一旦我们有了地图,我们就可以使用Object.values()来获取数组结果:

 var array = [ { group: "FL", list: [ { key: "A", value: "Alaska" }, { key: "B", value: "Brazil" }, { key: "C", value: "California" } ] }, { group: "NZ", list: [ { key: "A", value: "Alaska" }, { key: "B", value: "Brazil" }, { key: "D", value: "Delhi" } ] }, { group: "QA", list: [ { key: "A", value: "Alaska" }, { key: "B", value: "Brazil" }, { key: "C", value: "California" } ] } ] function getListKey(list) { return JSON.stringify(list.sort(({ key: a }, { key: b }) => a.localeCompare(b))); } const result = Object.values(array.reduce((acc, { group, list }) => { const key = getListKey(list); if (!acc[key]) { acc[key] = { group, list }; } else { acc[key].group += "," + group; } return acc; }, {})) console.log('Result:', result);
 .as-console-wrapper { max-height: 100% !important; top: 0; }

另一种方法是再次使用Array.reduce() ,但使用 lodash _.isEqual()函数进行列表比较。 这进行了深入的比较。 我们将它与Array.find()一起使用来获取任何重复的列表。

 var array = [ { group: "FL", list: [ { key: "A", value: "Alaska" }, { key: "B", value: "Brazil" }, { key: "C", value: "California" } ] }, { group: "NZ", list: [ { key: "A", value: "Alaska" }, { key: "B", value: "Brazil" }, { key: "D", value: "Delhi" } ] }, { group: "QA", list: [ { key: "A", value: "Alaska" }, { key: "B", value: "Brazil" }, { key: "C", value: "California" } ] } ] const result = array.reduce((acc, cur) => { const foundItem = acc.find(item => _.isEqual(item.list, cur.list)); if (foundItem) { foundItem.group += `,${cur.group}`; } else { acc.push(cur); } return acc; }, []) console.log('Result:', result);
 .as-console-wrapper { max-height: 100% !important; top: 0; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" referrerpolicy="no-referrer"></script>

你对async使用让你在这里绊倒,我不确定你使用它的原因。

为了使您的代码按原样工作,您需要在每次迭代时await累加器,并将reduce()的结果分配给某些东西。

 var array = [ { group: 'FL', list: [ { key: 'A', value: 'Alaska' }, { key: 'B', value: 'Brazil' }, { key: 'C', value: 'California' }, ], }, { group: 'NZ', list: [ { key: 'A', value: 'Alaska' }, { key: 'B', value: 'Brazil' }, { key: 'D', value: 'Delhi' }, ], }, { group: 'QA', list: [ { key: 'A', value: 'Alaska' }, { key: 'B', value: 'Brazil' }, { key: 'C', value: 'California' }, ], }, ]; function checkObjs(arr1, arr2) { const objectsEqual = (o1, o2) => Object.keys(o1).length === Object.keys(o2).length && Object.keys(o1).every((p) => o1[p] === o2[p]); return arr1.length === arr2.length && arr1.every((el, i) => objectsEqual(el, arr2[i])); } async function compareObjects(o1, o2) { for (let i = 0; i < o1.length; i++) { const value = await checkObjs(o1[i].list, o2.list); if (value) { return { index: i, group: o1[i].group }; } } } // assign the result of reduce to a variable const result = array.reduce(async (acc, item) => { acc = await acc; // await the returned accumulator Promise const exist = await compareObjects(acc, item); if (exist) { acc[exist.index].group = exist.group + ',' + item.group; } else { acc.push(item); } return acc; }, []); result.then((r) => console.log(r));
 .as-console-wrapper { max-height: 100% !important; top: 0; }

Reduce 不适用于async/await 如果您没有async代码 - 从 API 获取某些内容或使用来自 Promise 的数据,您应该删除async/await ,因为它是同步的。

如果您的代码使用了一些异步 API - 尝试使用类似的东西:

export const reduceAsync = async (array, transformer, initialvalue) => {
    let accumolator = typeof initialValue !== 'undefined' ? initialValue : array[0];

    for (let i = 0; i < array.length; i++) {
        accumolator = await transformer(accumolator, array[i], i, array);
    }

    return accumolator;
};

上面的函数是可重用的,并遵循此处定义的规范: https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

我认为我建议解决这个问题的方法是将其分解并(希望)使用库函数来解决一些更复杂的问题。 例如用lodash你可以说

import isEqual from "lodash/isEqual";

const arr = [
  {
    group: "FL",
    list: [
      { key: "A", value: "Alaska" },
      { key: "B", value: "Brazil" },
      { key: "C", value: "California" }
    ]
  },
  {
    group: "NZ",
    list: [
      { key: "A", value: "Alaska" },
      { key: "B", value: "Brazil" },
      { key: "D", value: "Delhi" }
    ]
  },
  {
    group: "QA",
    list: [
      { key: "A", value: "Alaska" },
      { key: "B", value: "Brazil" },
      { key: "C", value: "California" }
    ]
  }
];

function groupBy<T, R>(
  a: T[],
  iteritem: (t: T) => R,
  compare: (a: R, b: R) => boolean = isEqual
) {
  const groups: T[][] = [];
  const rs = a.map(iteritem);
  for (let i = 0; i < rs.length; i++) {
    let added = false;
    const r = rs[i];
    for (let j = 0; j < groups.length; j++) {
      if (compare(r, iteritem(groups[j][0]))) {
        groups[j].push(a[i]);
        added = true;
        break;
      }
    }
    if (!added) {
      groups.push([a[i]]);
    }
  }
  return groups;
}

const grouped = groupBy(arr, (a) => a.list);
const combined = [];
for (const g of grouped) {
  combined.push({
    group: g.map(({ group }) => group).join(","),
    list: g[0].list
  });
}
console.log(JSON.stringify(combined, undefined, 2));

这不是一次性的答案,因为groupBy可以重复使用。 我最初想使用groupBylodash但它不接受自定义相等函数。

这是一种可能的解决方案:

  const sorted = [];
  for (let i = 0; i < groups.length; i++) {
    const identicalLists = [];

    for (let j = i; j < groups.length; j++) {
      const isIdentical =
        JSON.stringify(groups[i].list) === JSON.stringify(groups[j].list);
      const found = !!sorted.flat().find((item) => item === groups[j].group);
      if (isIdentical && !found) {
        identicalLists.push(groups[j].group);
      }
    }
    if (identicalLists.length > 0) {
      sorted.push(identicalLists);
    }
  }

  const answer = sorted.map((item) => {
    const first = groups.find((group) => group.group === item[0]);
    return { group: item, list: first.list };
  });

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM