简体   繁体   中英

Combine arrays of objects by object index

I am filtering an array for every value the is the same as the key provided. Im certain there is a one shot reduce method someone better than me can condense this down to, but alas filter map filter map.

So I submit to an array an object that says [{k:v}, {k2:otherv}] and find all the elements that are not that and then return those object keys.

The code below returns:

  [
    {k: v1},
    {k: v2},
    {k: v3}
  ]
   [
    {k2: v4},
    {k2: v5},
    {k2: v6}
   ]
 ]

And obviously to map over it correctly id like it to look like [{k:v1, k2:v4}, {k:v2,k2:v5}, {k:v3, k2:v6}]

I've tried several examples from:

How can I merge two object arrays by index in JavaScript?

and

Combine same-index objects of two arrays

but short of writing every object key possible into each of these, none of what I've tried works.

const blogkeys = cont
   .filter((k) => k.type === "blogs")
   .map(({ key, content }) => {
    if (key.includes(".")) {
     let objkey = key.substr(key.indexOf(".") + 1, key.length);

     let obj = { [objkey]: content };

     let arrName = key.substr(0, key.indexOf("."));

     let pushedObj = { [arrName]: [{ ...obj }] };

     return pushedObj;
    } else {
     let obj = { [key]: content };
     return obj;
    }
   });

this creates the keys we are looking for in the parent array


  const everyOtherBlog = blogkeys.map((blogkey) => {
   const returned = blogs
    .filter(
     (f) =>
      !JSON.stringify(f).includes(
       JSON.stringify(blogkey).replace("{", "").replace("}", "")
      )
    )
    .map(({ _doc }) => {
     let obj = {};

     Object.keys(_doc)
      .filter((f) => f === Object.keys(blogkey)[0])
      .map((a) => {
       obj = Object.assign(obj, { [a]: _doc[a] });

       return obj;
      });

     return obj[0];
    });

   return returned;
  });

This returns the data set you see.

Here is what blogkeys looks like:

[0] [
[0]   { title: ' stuff' },
[0]   {
[0]     p1: ' stuff '
[0]   }
[0] ]

which is made from

  {
[0]     _id: '606a4049d4812928986afc10',
[0]     contentId: '60443ced4e233336f8306b5b',
[0]     type: 'blogs',
[0]     key: 'title',
[0]     content: 'stuff'
[0]   },

and a blog looks something like

{
 title: '',
 p1:''
}

Everyone here provided alot of cool stuff that ended up not helping me because of how i was feeding the data in, when i fixed that i realized i didnt need any fancy zips just good old object.fromEntries. Ill leave this up though cause some of these are very interesting.

Any help would be great

two arrays

You can use map to implement zip and then map again to perform your tranform. This solution works for only two input arrays -

 const zip = (a, b) => a.map((x, i) => [x, b[i]]) const foo = [{a:1},{a:2},{a:3}] const bar = [{b:4},{b:5},{b:6}] const result = zip(foo, bar).map(o => Object.assign({}, ...o)) console.log(JSON.stringify(result))

[{"a":1,"b":4},{"a":2,"b":5},{"a":3,"b":6}]

many arrays, any size

Above, you will run into strange output if a or b is longer than the other. I think a better approach is to use generators though. It works for any number of input arrays of any size -

 const iter = t => t?.[Symbol.iterator]() function* zip (...its) { let r, g = its.map(iter) while (true) { r = g.map(it => it.next()) if (r.some(v => v.done)) return yield r.map(v => v.value) } } const foo = [{a:1},{a:2},{a:3}] const bar = [{b:4},{b:5},{b:6}] const qux = [{c:7},{c:8}] const result = Array.from(zip(foo, bar, qux), o => Object.assign({}, ...o)) console.log(JSON.stringify(result))

This does the zipping and transformation in a single pass, without the need map afterward -

[{"a":1,"b":4,"c":7},{"a":2,"b":5,"c":8}]

without generators

If you don't like generators but still want the flexibility offered by the solution above, we can write a simple zip2 -

const zip2 = ([a, ...nexta], [b, ...nextb]) =>
  a == null || b == null
    ? []                                   // empty
    : [ [a, b], ...zip2(nexta, nextb) ]    // recur

And then the variadiac zip which accepts any amount of arrays of any size -

const zip = (t, ...more) =>
  more.length
    ? zip2(t, zip(...more)).map(([a, b]) => [a, ...b]) // flatten
    : t.map(a => [a])                                  // singleton

Now we can zip any amount of arrays -

const foo =
  [{a:1},{a:2},{a:3}]

const bar =
  [{b:4},{b:5},{b:6}]

const qux =
  [{c:7},{c:8}]

const result =
  zip(foo, bar, qux).map(o => Object.assign({}, ...o))

console.log(JSON.stringify(result))

Expand the snippet below to verify the result in your own browser -

 const zip2 = ([a, ...nexta], [b, ...nextb]) => a == null || b == null? []: [ [a, b], ...zip2(nexta, nextb) ] const zip = (t, ...more) => more.length? Array.from(zip2(t, zip(...more)), ([a, b]) => [a, ...b]): t.map(a => [a]) const foo = [{a:1},{a:2},{a:3}] const bar = [{b:4},{b:5},{b:6}] const qux = [{c:7},{c:8}] const result = zip(foo, bar, qux).map(o => Object.assign({}, ...o)) console.log(JSON.stringify(result))

[{"a":1,"b":4,"c":7},{"a":2,"b":5,"c":8}]

Here's a fairly straightforward solution using .reduce() that will accept any number of arrays of various lengths.

 const foo = [{ a: 1 }, { a: 2 }, { a: 3 }], bar = [{ b: 4 }, { b: 5 }, { b: 6 }], qux = [{ c: 7 }, { c: 8 }], zip = (...arrs) => arrs.reduce((a, arr) => { arr.forEach((x, i) => Object.assign((a[i] = a[i] || {}), x)); // or using logical nullish assignment // arr.forEach((x, i) => Object.assign((a[i]??= {}), x)); return a; }, []); result = zip(foo, bar, qux); console.log(JSON.stringify(result)) // [{ a: 1, b: 4, c: 7 }, { a: 2, b: 5, c: 8 }, { a: 3, b: 6 }]

You can try this too with map and reduce , this is just another alternative

 function merge(...args) { // finding highest length Array to not skip missing elements from other arrays // for skipping missing elements use "acc.length < ele.length" const maxArray = args.reduce((acc, ele) => acc.length > ele.length? acc: ele); //Iterating over highest length array return maxArray.map((ele, index) => //merging all the instances in arrays with same index args.reduce((acc, group) => Object.assign(acc, group[index]), {}) ); } merge([ {k: 'v1'}, {k: 'v2'}, {k: 'v3'} ], [ {k2: 'v4'}, {k2: 'v5'}, {k2: 'v6'} ]); // [{"k":"v1","k2":"v4"},{"k":"v2","k2":"v5"},{"k":"v3","k2":"v6"}] merge([ {k: 'v1'}, {k: 'v2'}], [ {k2: 'v4'}, {k2: 'v5'}, {k2: 'v6'} ]) // [{"k":"v1","k2":"v4"},{"k":"v2","k2":"v5"},{"k2":"v6"}] merge([ {k: 'v1'}, {k: 'v2'}, {k: 'v3'} ], [ {k2: 'v4'}, {k2: 'v5'}]) //[{"k":"v1","k2":"v4"},{"k":"v2","k2":"v5"},{"k":"v3"}]

I wanted to share what I ended up doing cause it worked well with both nested arrays and simple object arrays and is formatted for getting info straight from an await from mongo db, sadly its just a filter map tho.

blog obj is { title:"stuff", p1:"stuff" }

and the return is the zipped array.

  const everyOtherBlog = Object.values(blogObj).map((val) => {
   const b = blogs
    .filter((f) => !JSON.stringify(f).includes(val))
    .map(({ _doc }) => {
     const keys = Object.keys(_doc).filter((k) =>
      Object.keys(blogObj).includes(k)
     );

     const entryObj = Object.fromEntries(keys.map((k) => [k, _doc[k]]));

     return entryObj;
    });

   return b[0];
  });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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