-1

I have an array that looks like this:

enter image description here

So as a strucutre it would be something like:

[
    [
       { classNumber: '2', status: 'A', terms: [] }, 
       { classNumber: '32', status: 'B', terms: [] }, 
       { classNumber: '44', status: 'C', terms: []  }
    ],
    [
        { classNumber: '2', status: 'B', terms: [] }, 
        { classNumber: '31', status: 'A', terms: [] }
    ],
    ....
]

This wierd array of objects happens because at some point, in our app, we are creating an array of reasons to object something using the same object.

I need to be able to merge the nested array of objects to look like this:

[
     { classNumber: '2', status: [ 'A', 'B' ], terms: [] }, 
     { classNumber: '31', status: [ 'A' ], terms: [] }, 
     { classNumber: '32', status: [ 'B' ], terms: [] }, 
     { classNumber: '44', status: [ 'C' ], terms: [] }
]

But I've been struggling with this for some days, looking for some lodash functions but still no luck...

I'm completely lost on how to achieve this. All examples look simpler, with less nested arrays. Any idea on how to merge all props for the same object key?

Thanks a lot in advance.

Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
Sonhja
  • 8,230
  • 20
  • 73
  • 131
  • 1
    `allPropsFromAllNestedArraysTogether` makes short work of some complex decisions. For instance, what do you do about the same property with different values (e.g., `status` in your example data)? – Heretic Monkey May 09 '22 at 14:22
  • For all of them I'm planning to do a generic solution. In the `status` example, create an array with all the `status` (at least for now). But first I need to be able to merge that and then I'll decide if I make that operations more complex. Does it have sense? – Sonhja May 09 '22 at 14:24
  • I think `{ classNumber: '2', {...allPropsFromAllNestedArraysTogether }` might be an invalid JSON format. Since, you need a key/value, key/array, or key/object. – acarlstein May 09 '22 at 14:29
  • Agree @acarlstein . It's just an example saying that all props would be there. Gonna correct it so it will be more clear. – Sonhja May 09 '22 at 14:30
  • Edited the description so it can give a more clear vision. – Sonhja May 09 '22 at 14:31
  • 1
    What you're talking about is grouping, and there are solutions on Stack Overflow already, depending on how you want to deal with the complexity. For instance, [Group array items using object](https://stackoverflow.com/q/31688459/215552). There's also merging, which has [Merge JavaScript objects in array with same key](https://stackoverflow.com/q/33850412/215552) – Heretic Monkey May 09 '22 at 14:38
  • 1
    @acarlstein The question has nothing to do with JSON. The code in the question is JavaScript and can't be JSON. JSON requires double quotes for keys and strings. – jabaa May 09 '22 at 14:40
  • @jabaa after I pointed out that the JSON expected was invalid, the question has being updated. While the question wasn't about the JSON, it was important to ask a clarification of the expected result. – acarlstein May 09 '22 at 15:03
  • @jabaa, I asked for a clarification because I noticed that the example (which was corrected) didn't follow the JSON format. Is that wrong to ask for clarifications? Do this matters? – acarlstein May 09 '22 at 19:15
  • @jabaa I will put it in a different way, and I hope this ends the long discussion. While the input example was clear, the output example wasn't. The good thing is that by pointing out that the output provided wasn't following the JSON standards, the author of the question update it. This make it easier to everyone in order to provide a solution to the author of the question. – acarlstein May 09 '22 at 19:29
  • @acarlstein The input is still not following the JSON standards. The code in the question is not valid JSON. You can try to validate it using https://jsonlint.com/. JSON is not JavaScript and JavaScript is not JSON. This question has nothing to do with JSON. – jabaa May 09 '22 at 19:36

3 Answers3

1

From the OP's own comment ...

"For all of them I'm planning to do a generic solution. In the status example, create an array with all the status (at least for now). But first I need to be able to merge that and then I'll decide if I make that operations more complex. Does it have sense?" – Sonhja

A generic solution which handles any (flattened) array item regardless of such an item's data structure most probably has to come up with a two folded approach ...

  • a generic reducer function which reduces the flattened array's data-items by grouping any data item which for a provided key (property name) features the same value.

  • a custom implemented merge function (which gets provided as part of the reduce method's initialValue) where one can decide how a data-item's entry (which is distinct from the grouping property name) is going to be merged into the target data structure.

// reducer function which generically
// - groups any data item by a provided key's same value
// - and merges all other entries via a custom merge function.
function groupBySameKeyValueAndMergeProperties(collector, item) {
  const { merge, key, lookup, result } = collector;
  const { [key]: groupValue, ...rest } = item;

  let groupItem = lookup.get(groupValue);
  if (!groupItem) {

    groupItem = { [key]: groupValue };

    lookup.set(groupValue, groupItem);
    result.push(groupItem);
  }
  merge(groupItem, rest);

  return collector;
}

// custom, task specific merge function, according to the OP's goal.
function mergeDataItemEntries(targetItem, sourceItem) {
  Object
    .entries(sourceItem)
    .reduce((target, [key, value], idx, arr) => {

      if (target.hasOwnProperty(key)) {

        // collect value of currently processed entry.
        target[key].push(value);

      } else {

        // initial (one time) array initialization
        // in order to later collect all other values.
        target[key] = [value];
      }
      // here, maybe even a final treatment
      // for the 2 dimensional `terms` array.
      if (idx >= arr.length - 1) {

        // - flattening of the 2 dimensional `terms` array.
        // - a `Set` instance always assures unique values.
        target.terms = [...new Set(target.terms.flat())];
      }
      return target;

    }, targetItem);
}

const sampleData = [
  [{
    classNumber: '2', status: 'A', terms: ['foo', 'bar'],
  }, {
    classNumber: '32', status: 'B', terms: ['baz'],
  }, {
    classNumber: '44', status: 'C', terms: ['bizz'],
  }], [{
    classNumber: '2', status: 'B', terms: ['bar', 'baz'],
  }, {
    classNumber: '31', status: 'A', terms: ['buzz'],
  }],
];
console.log(
  'flattened, merged and sorted data items ...',
  sampleData
    .flat()
    .reduce(groupBySameKeyValueAndMergeProperties, {

      // task specific reduce configuration.
      merge: mergeDataItemEntries,
      key: 'classNumber',
      lookup: new Map,
      result: [],
    })
    .result
    .sort((a, b) =>
      Number(a.classNumber) - Number(b.classNumber)
    )
);
console.log('original data structure ...', sampleData);

console.log(
  '... Bonus .. the pure grouping result without merging and sorting ...',
  sampleData
    .flat()
    .reduce(groupBySameKeyValueAndMergeProperties, {

      merge: (_ => _),
      key: 'classNumber',
      lookup: new Map,
      result: [],

    }).result
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
-1

If I'm not missing something, you are looking for this:?

var merged = Array.prototype.concat.apply([], original);

like:

Array.prototype.concat.apply([], [[1,2,3],[4,5], [6]]);
// returns:
// [1, 2, 3, 4, 5, 6]

Another way:

var merged = [];
for(var i = 0; i<original.length; i++) {
  for(var j = 0, arr = original[i]; j<arr.length; j++) {
    merged.push(arr[j]);
  }
}
    

update:

Yes, I DO HAVE MISSED SOMETHING. Thanks @PeterSeliger for pointing it out. But instead of delete this answer, I'd like to update and correct it.

The code below is NOT TESTED.


function doMergeItems(prev, current) {
  // Merge content of "current" with "prev"
  // Below is just a simple example, need to be replaced
  // according to the desired merging strategy.
  if(!prev)
    prev = {
      classNumber: current.classNumber,
      status: [],
      terms: [],
    };
  prev.status.push(current.status);
  prev.terms.push(...current.terms);
  return prev;
}


var merged = [];
for(var i = 0; i<original.length; i++) {
  for(var j = 0, arr = original[i]; j<arr.length; j++) {
    var item = arr[j];
    var index = merged.findIndex(function(x){
      return x.classNumber === item.classNumber;
    });
    if(index < 0)
      merged.push(doMergeItems(null, item));
    else
      merged[index] = doMergeItems(merged[index], item);
  }
}

Todd Wong
  • 234
  • 1
  • 4
  • Why not `.flat`? Why do you use `.apply`? – jabaa May 09 '22 at 14:42
  • @jabaa Good idea! (IIRC, it is relatively new, actually I've never used it myself, so it may have some more stricter brower/environment requirement?). – Todd Wong May 09 '22 at 14:48
  • 7 years is not relatively new. – jabaa May 09 '22 at 14:49
  • I used an initial solution pretty similar to the first one you posted. So I think this would be the solution :) I will post what I did once my code is more clear. Thanks for this one! PS: I didn't use the `apply`, just the concat – Sonhja May 09 '22 at 14:50
  • @jabaa 4 years according to caniuse.com. @Sonhja without apply you may need a loop or the spread operator(which is relatively new, i mean relatively): `[].concat(...original)` I think – Todd Wong May 09 '22 at 14:54
  • @PeterSeliger Thanks for point it out. I do missed something. I'll edit my answer – Todd Wong May 10 '22 at 08:59
  • @PeterSeliger No, not test. Just a simple solution, easy to read and easy to understand, and easy to customize. Sorry for the bugs, but I do believe they are obvious and easy to fix. – Todd Wong May 10 '22 at 10:56
  • @ToddWong ... _"Sorry for the bugs, but I do believe they are obvious and easy to fix"_ ... Really? ... This is the task of the one who proudly presents a solution. Please provide next time [executable example code](https://meta.stackoverflow.com/questions/358992/ive-been-told-to-create-a-runnable-example-with-stack-snippets-how-do-i-do) where one (including you) immediately can countercheck / test / verify the approach and its implemented code. – Peter Seliger May 10 '22 at 11:30
  • @PeterSeliger I'm not that proudly presents. Just wanna be helpful. I don't think OP is seeking a piece of working code, but an idea that can be absorbed and fit in his/her own problems. My code is just a reference, a tip. – Todd Wong May 10 '22 at 11:41
  • @ToddWong _**"... but an idea that can be absorbed and fit in his/her [OP's] own problems"**_ ... needs to be bug-free in the very first in order to be digestible by / comprehensible to the OP, all the more so since there is nothing else provided (like additional explanation/s to the approach) than just bare code. – Peter Seliger May 10 '22 at 11:54
  • @PeterSeliger That's what you think and I respect. But if I asked a question and received an answer, I won't deny its value just because it is not tested or buggy. I'll ask for knowledge, not homework. And good(simple) code needs no additional explanation. – Todd Wong May 10 '22 at 12:13
-1

I made it quick but something like this should do the job

const doubleNestedArray = [
  [{
    classNumber: '2', status: 'A', terms: ['foo', 'bar'],
  }, {
    classNumber: '32', status: 'B', terms: ['baz'],
  }, {
    classNumber: '44', status: 'C', terms: ['bizz'],
  }], [{
    classNumber: '2', status: 'B', terms: ['bar', 'baz'],
  }, {
    classNumber: '31', status: 'A', terms: ['buzz'],
  }],
];
console.log(
  _.chain(doubleNestedArray)
    .flatten()
    .groupBy(element => element.classNumber)
    .map((value) => ({
        ...value[0],
        status: value.map(element => element.status)
    }))
    .value()
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
  • 1
    @Sonhja ... With editing and providing the above code as executable SO snippet I can see why someone would downvote the approach. Nevertheless this attempt already is much closer (also taking into account the OP's `lodash` tag) to what the OP is looking for than what the OP considers being the accepted answer. – Peter Seliger May 09 '22 at 16:31