如何在某些公共字段上将两个列表合并为一个列表?

How to join two list into one on some common fields?

我想使用深拷贝将两个列表合二为一。
例如

list1:
[
   {"key1":["val1"]}, 
   {"key2":["val2", "val3"]}
]

list2:
[
   {"key2":["val2", "val4"]},
   {"key3":["val5"]}
]

I want the output to be :
[
   {"key1":["val1"]}, 
   {"key2":["val2", "val3", "val4"]}
   {"key3":["val5"]}
]

我试过 std.mergePatch 但它只是覆盖了之前的列表。 谢谢,

你有相当复杂的结构要合并,没有标准函数在这里有帮助,自定义编写的函数将在很大程度上取决于你想要什么

如果您不介意数组元素的顺序,并且键不重复,则此方法可行:


local
// Extract "key" out of {key: ...}
unionName(u) = assert std.length(u) == 1; std.objectFields(u)[0],
// Convert [{a: ...}, {b: ...}] to {a: ..., b: ...}
mergeArrsToObject(arr) = {
  [unionName(el)]: el[unionName(el)] for el in arr
},
// Inverse of mergeArrsToObject
splitArrsFromObject(obj) = [
  {[key]: obj[key]} for key in std.objectFields(obj)
],
// Custom merge logic implemented for object:
// here I add values from both, and then sort & deduplicate
mergeObjects(a, b) = {
  [key]: std.set(std.get(a, key, []) + std.get(b, key, []))
  for key in std.set(std.objectFields(a) + std.objectFields(b))
},
merge(a, b) = splitArrsFromObject(mergeObjects(
  mergeArrsToObject(a),
  mergeArrsToObject(b),
))
;

merge([
  {"key1":["val1"]}, 
  {"key2":["val2", "val3"]}
], [
  {"key2":["val2", "val4"]},
  {"key3":["val5"]}
]) == [
  {"key1":["val1"]},
  {"key2":["val2", "val3", "val4"]},
  {"key3":["val5"]}
]

在下面复制一个可能的解决方案,将列表“展开”为单个 key-value 条目,然后将它们聚合到显示的对象中,请注意,由于详细的注释,代码很长。

local list1 = [
  { key1: ['val1'] },
  { key2: ['val2', 'val3'] },
];

local list2 = [
  { key2: ['val2', 'val4'] },
  { key3: ['val5'] },
];

// Hash key for the `seen` dictionary
local hash(kv) = std.md5(std.toString(kv));

// Loop over the passed array of objs, aggregating each final/"leaf" KV pair,
// but only if not seen before
local mergeLists(lists) = std.foldl(
  function(mergedObj, kv) mergedObj {
    ret+: if (hash(kv) in mergedObj.seen) then {} else {
      [kv.key]+: [kv.value],
    },
    seen+:: {
      [hash(kv)]: true,
    },
  },
  // "Unroll" the (sum of) list(s), we need 3 loops:
  // 1) main list
  // 2) each entry's object from 1)
  // 3) each "final" list of values
  // -> convert them into single Key-Value (mini) objects
  [
    { key: k, value: v }
    for list in lists
    for k in std.objectFields(list)
    for v in list[k]
  ],
  { seen:: {} },
);

mergeLists(list1 + list2).ret