【问题标题】:Compare two json objects and append unique items比较两个 json 对象并附加唯一项
【发布时间】:2021-08-29 05:03:43
【问题描述】:

我有两个 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" }
]

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

【问题讨论】:

  • 一个懒惰的黑客来快速完成这个是使用Set
  • @SamridhTuladhar 这行不通,因为包含相同值的对象仍然具有不同的身份。例如。 new Set([{foo: 'bar'}, {foo: 'bar'}, 1, 1]) 将产生一个带有{foo: 'bar'} 两次 的集合,并且正如预期的那样,产生一个 1。

标签: javascript node.js unique


【解决方案1】:

您可以使用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));

【讨论】:

  • 谢谢你 - 它似乎在 sn-p 中有效,但在 nodejs 中无效。我在??= 收到错误消息。我尝试搜索它以查看它是什么但找不到任何东西..
  • 您可以使用r[key] = r[key] || o 代替r[key] ??= o;?? 是 Nullish Coalescing 运算符。
【解决方案2】:

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

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"
  }
]
*/

【讨论】:

    【解决方案3】:

    您可以按国家/地区对 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。我们可以使用相同的 reducer:

    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) 复制它,否则当它的值从你下面发生变化时你可能会感到惊讶.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-02
      • 1970-01-01
      • 2018-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多