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

541 views Asked by At

I want to join two list into one using deep copy.
E.g.

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

I tried std.mergePatch but it just overrides the previous list. Thanks,

2

There are 2 answers

0
jjo On BEST ANSWER

Copying below a possible solution, which "unrolls" the list(s) down to single key-value entries, then aggregate them into the manifested object, note that the code is long because of detailed comments.

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
0
Yaroslav Bolyukin On

You have pretty complex structure to merge, no standard functions will help here, and custom written ones will highly depend on what do you want

If you don't mind order of array elements, and keys doesn't repeat, then this will work:


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