比较两个 json 个对象并追加唯一项

Compare two json objects and append unique items

我有两个 JSON 文件 - 一个是从服务器获取的 (data),另一个是本地的 (database)。

我正在尝试遍历服务器 json,将其与本地 json 进行比较,然后将任何新对象附加到本地文件。这样,当脚本 运行 第二次出现时,这些新项目已被追加并可以跳过。

问题:

当前,当它在初始时间后 运行 秒时,因为它检查 a-a, a-b, a-c, b-a, b-b, b-c... 它将跳过初始结果,但随后添加其他选项。

我正在使用的js:

let additions = [];
data.forEach(item1 => {
  database.filter(item2 => {
    if( !(item1.value === item2.value && item1.country === item2.country) ) {
      additions.push(item1)
    }
  })
});

示例/期望:

data = [
  { value: "qwert", country: "US" },
  { value: "asdfg", country: "CA" },
  { value: "zxcvb", country: "GB" }
] 

database = [
  { value: "poiuy", country: "CA" },
  { value: "qwert", country: "US" }
]

然后在循环和检查之后,结果如下(初始运行):

output = [
  { value: "poiuy", country: "CA" },
  { value: "qwert", country: "US" },
  { value: "asdfg", country: "CA" },
  { value: "zxcvb", country: "GB" }
] 

当我运行下一次循环时,如果数据发生变化:

data = [
  { value: "poiuy", country: "CA" },
  { value: "wsxcd", country: "AU" },
  { value: "rfvbg", country: "NZ" },
  { value: "zxcvb", country: "FR" }
]

我期待这个:

output = [
  { value: "poiuy", country: "CA" },
  { value: "qwert", country: "US" },
  { value: "asdfg", country: "CA" },
  { value: "zxcvb", country: "GB" },
  { value: "wsxcd", country: "AU" },
  { value: "rfvbg", country: "NZ" },
  { value: "zxcvb", country: "FR" }
]

但我收到了这个:

output = [
  { value: "poiuy", country: "CA" },
  { value: "qwert", country: "US" },
  { value: "asdfg", country: "CA" },
  { value: "zxcvb", country: "GB" },
  { value: "poiuy", country: "CA" },
  { value: "poiuy", country: "CA" },
  { value: "poiuy", country: "CA" },
  { value: "wsxcd", country: "AU" },
  { value: "wsxcd", country: "AU" },
  { value: "wsxcd", country: "AU" },
  { value: "rfvbg", country: "NZ" },
  { value: "rfvbg", country: "NZ" },
  { value: "rfvbg", country: "NZ" },
  { value: "zxcvb", country: "FR" },
  { value: "zxcvb", country: "FR" },
  { value: "zxcvb", country: "FR" }
]

任何帮助将不胜感激,因为我似乎无法弄清楚它只会附加唯一的项目,而不是逐项附加。

如果我没理解错的话,最简单但不是最快的方法就是简单地合并数组然后过滤唯一值。

let data = [
  { value: "qwert", country: "US" },
  { value: "asdfg", country: "CA" },
  { value: "zxcvb", country: "GB" }
] 

let database = [
  { value: "poiuy", country: "CA" },
  { value: "qwert", country: "US" }
]

const getUniqueUnion = (data1, data2) => {
  return [...data1, ...data2].reduce((acc, cur) => {
    const match = acc.find((el) => {
      return el.country === cur.country && el.value === cur.value;
    });
    
    if (!match) {
      acc.push(cur);
    }
    
    return acc;
  }, []);
}

const result = getUniqueUnion(data, database);
console.log(result);

/*
  [
  {
    "value": "qwert",
    "country": "US"
  },
  {
    "value": "asdfg",
    "country": "CA"
  },
  {
    "value": "zxcvb",
    "country": "GB"
  },
  {
    "value": "poiuy",
    "country": "CA"
  }
]
*/

data = [
  { value: "poiuy", country: "CA" },
  { value: "wsxcd", country: "AU" },
  { value: "rfvbg", country: "NZ" },
  { value: "zxcvb", country: "FR" }
];

const result2 = getUniqueUnion(data, database);

console.log(result2);

/*
  [
  {
    "value": "poiuy",
    "country": "CA"
  },
  {
    "value": "wsxcd",
    "country": "AU"
  },
  {
    "value": "rfvbg",
    "country": "NZ"
  },
  {
    "value": "zxcvb",
    "country": "FR"
  },
  {
    "value": "qwert",
    "country": "US"
  }
]
*/

您可以使用 array#reduce 根据对象累加器中的 valuecountry 获取唯一值。然后使用 Object.values().

从这些对象中获取所有值

const localData = [ { value: "qwert", country: "US" }, { value: "asdfg", country: "CA" }, { value: "zxcvb", country: "GB" }],
      database = [{ value: "poiuy", country: "CA" }, { value: "qwert", country: "US" }],
      getUnique = (arr1, arr2) => Object.values([...arr1, ...arr2].reduce((r,o) => {
        const key = `${o.value}_${o.country}`;
        r[key] ??= o;
        return r;
      },{}));
console.log(getUnique(localData, database));

您可以按国家/地区对 database 数组进行分组,从而生成从国家/地区到值集的映射。使用哈希映射和集合将避免重复。

要分组做:

let reducer = (acc, curr) => {
  let {country, value} = curr;

  if (acc.hasOwnProperty(country)) {
    acc[country].add(value);
  } else {
    acc[country] = new Set([value]);
  }

  return acc;
};
let groupedDatabase = database.reduce(reducer, {});

然后循环 data 并将每个条目分配给 groupedDatabase。我们可以为此使用相同的减速器:

let intermediateResult = data.reduce(reducer, groupedDatabase);

每次data变化,更新intermediateResult

intermediateResult = data.reduce(reducer, groupedDatabase);

最后,将分组数据转回数组:

let output = Object.keys(intermediateResult).reduce((acc, curr) => {
  let entries = Array.from(intermediateResult[curr]).map(value => {
    return {country: curr, value}
   });

  acc.push(...entries);
  return acc;
}, []);

这是假设您只是为了避免重复而不是出于性能原因而想跳过看到的记录。

旁注: reducer 改变传递的初始值,例如 groupedDatabase。这增强了您使用完全相同的函数调用(data.reduce(reducer, groupedDatabase); 部分)更新 intermediateResult 的能力。由于突变,请务必仅在此上下文中使用 groupedDatabase。如果你最终想在其他地方使用它(虽然我认为你不会,因为我介绍它只是为了这个目的),首先使用 let copiedGroupedDatabase = Object.assign({}, groupedDatabase) 复制它,否则当它的值从在你下面