3

i'm trying to regroupe a complexe array of object.

Here is my array :

[ 
  { scenario: "Treasury", diagnostic: "good results", action: "Manage Financial Recovery"},
  { scenario: "Treasury", diagnostic: "good results", action: "Analyze the impact of your investments"},
  { scenario: "Treasury", diagnostic: "Significant decline", action: "Ensure an adequate"},
  { scenario: "Treasury", diagnostic: "Significant decline", action: "Pilot your cash"},
  { scenario: "Turnover", diagnostic: "Improve trade efficiency of your business", action: "Valorize your labels"},
  { scenario: "Turnover", diagnostic: "Improve trade efficiency of your business", action: "Analyze the opportunity"}
  { scenario: "Turnover", diagnostic: "Improve trade efficiency of your firm", action: "Contacter un prestataire"}
];

I want to unify the array above to this one :

[ 
  { 
    scenario: "Treasury",
    diagnostics: [
        {
            diagnostic : "good results",
            actions: [
                "Manage Financial Recovery",
                "Analyze the impact of your investments"
            ]
        }
        {
            diagnostic : "Significant decline",
            actions: [
                "Ensure an adequate",
                "Pilot your cash"
            ]
        }
    ]
  },
  { 
    scenario: "Turnover",
    diagnostics: [
        {
            diagnostic : "Improve trade efficiency of your business",
            actions: [
                "Valorize your labels",
                "Analyze the opportunity"
            ]
        }
        {
            diagnostic : "Improve trade efficiency of your firm",
            actions: [
                "Contacter un prestataire"
            ]
        }
    ]
  }
];

So i tried to unify my array using JSBin but i did not get the expected result, so what is the most efficient method to get an array of no duplicated ojects.

Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
Main Flow
  • 319
  • 2
  • 18
  • Possible duplicate of [What is the most efficient method to groupby on a javascript array of objects?](http://stackoverflow.com/questions/14446511/what-is-the-most-efficient-method-to-groupby-on-a-javascript-array-of-objects) – Heretic Monkey Jan 23 '17 at 17:19
  • possible but my array and my expected result is more complicated and the answears did not help me to solve my probleme in the other question the user want to unify two keys by in my array i have many duplicated keys to be unified – Main Flow Jan 23 '17 at 17:24
  • Try http://stackoverflow.com/q/36196298/215552. This question and questions like it have been asked repeatedly on SO. – Heretic Monkey Jan 23 '17 at 17:27
  • Thank you @MikeMcCaughan it's different but I will try to adapt – Main Flow Jan 23 '17 at 17:30
  • @MaherMahouachi Is the input array structure same all the time or else you want the functionality for a dynamic array? – Nikhilesh Shivarathri Jan 23 '17 at 17:38
  • @NikhileshShivarathri it's a dynamic array, the number of objects can change by adding new or same object with different actions – Main Flow Jan 23 '17 at 17:42

3 Answers3

1

You could use an iterative approach and use a helper object for the grouped items of key.

function getGrouped(array, keys, groupCB, children) {
    var result = [],
        hash = { _: result };

    groupCB = groupCB || function (o) { return o; };
    children = children || [];
    array.forEach(function (a) {
        keys.reduce(function (r, k, i) {
            var o = {};
            if (!r[a[k]]) {
                r[a[k]] = { _: [] };
                o[k] = a[k];
                o[children[i] || 'children'] = r[a[k]]._;
                r._.push(o);
            }
            return r[a[k]];
        }, hash)._.push(groupCB(a));
    });
    return result;
}

var data = [{ scenario: "Treasury", diagnostic: "good results", action: "Manage Financial Recovery" }, { scenario: "Treasury", diagnostic: "good results", action: "Analyze the impact of your investments" }, { scenario: "Treasury", diagnostic: "Significant decline", action: "Ensure an adequate" }, { scenario: "Treasury", diagnostic: "Significant decline", action: "Pilot your cash" }, { scenario: "Turnover", diagnostic: "Improve trade efficiency of your business", action: "Valorize your labels" }, { scenario: "Turnover", diagnostic: "Improve trade efficiency of your business", action: "Analyze the opportunity" }, { scenario: "Turnover", diagnostic: "Improve trade efficiency of your firm", action: "Contacter un prestataire" }],
    groupCB = function (o) { return o.action },
    keys = ['scenario', 'diagnostic'],
    children = ['diagnostics', 'actions'],
    result = getGrouped(data, keys, groupCB, children);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • 1
    Great Implementation! But it would've been much useful if the code is legible. – Nikhilesh Shivarathri Jan 23 '17 at 17:54
  • Good idea, the fact that you used an array of keys! it's the result that i tried to get! – Main Flow Jan 23 '17 at 17:58
  • the advantage of the above, it can be used for more keys/children, if you need it, without changing the main algorithm. – Nina Scholz Jan 23 '17 at 18:01
  • Though this solution does its job, already its approach makes it difficult to read and hard to refactor/reuse. It unnecessarily nests two iteration cycles and misuses `thisArgs` of the outer `forEach`, especially since the approach already depends on additional array structures like `keys`, `children`, `result` for read/write. A much cleaner purely `reduce` based approach encapsulates the algorithm into a single collector function and injects all necessary read/write structures into `reduce` as its `startValue`, thus improving readability, refactoring and reuse of such encapsulated algorithms. – Peter Seliger Jan 24 '17 at 14:27
  • @PeterSeliger, imagine, you need two other groups as well, like for example a `risk` and a `opportunity` property, then i could add both keys to the `keys` array and insert the group names to `children` array and use the same code as above for a more grouped result than before. – Nina Scholz Jan 24 '17 at 16:14
0

You may do as follows:;

var data = [ { scenario: "Treasury", diagnostic: "good results", action: "Manage Financial Recovery"},
             { scenario: "Treasury", diagnostic: "good results", action: "Analyze the impact of your investments"},
             { scenario: "Treasury", diagnostic: "Significant decline", action: "Ensure an adequate"},
             { scenario: "Treasury", diagnostic: "Significant decline", action: "Pilot your cash"},
             { scenario: "Turnover", diagnostic: "Improve trade efficiency of your business", action: "Valorize your labels"},
             { scenario: "Turnover", diagnostic: "Improve trade efficiency of your business", action: "Analyze the opportunity"},
             { scenario: "Turnover", diagnostic: "Improve trade efficiency of your firm", action: "Contacter un prestataire"}
           ],
    hash = data.reduce(function(p,c){
                         var fd = null;
                         p[c.scenario] = p[c.scenario] ? (fd = p[c.scenario].diagnostics.find(d => d.diagnostic === c.diagnostic),
                                                          fd ? (fd.action.push(c.action),p[c.scenario])
                                                             : (p[c.scenario].diagnostics.push({diagnostic: c.diagnostic, action: [c.action]}),p[c.scenario]))
                                                       : {scenario: c.scenario, diagnostics: [{diagnostic: c.diagnostic, action: [c.action]}]};
                         return p;
                       },{});
  result = Object.keys(hash).map(k => hash[k]);
console.log(JSON.stringify(result,null,4));
Redu
  • 25,060
  • 6
  • 56
  • 76
-1

One also might consider implementing a sole collector function that just uses lookups into locally injected references, thus, applied within a single reduce iteration/cycle, it already aggregates/creates the desired data structure.

There is really no need of nesting a reduce cycle into a forEach iteration as demonstrated with Nina's approach that also havily bends the usage of tha latter's thisArgs. (And not only for that is hard to read).

Redu's approach uses reduce, however within each iteration step there will be a find that is an iterator too ... the final result depends on jet another two iterations, keys and map.

human readable example code ...

var diagnosticData = [
  { scenario: "Treasury", diagnostic: "good results", action: "Manage Financial Recovery"},
  { scenario: "Treasury", diagnostic: "good results", action: "Analyze the impact of your investments"},
  { scenario: "Treasury", diagnostic: "Significant decline", action: "Ensure an adequate"},
  { scenario: "Treasury", diagnostic: "Significant decline", action: "Pilot your cash"},
  { scenario: "Turnover", diagnostic: "Improve trade efficiency of your business", action: "Valorize your labels"},
  { scenario: "Turnover", diagnostic: "Improve trade efficiency of your business", action: "Analyze the opportunity"},
  { scenario: "Turnover", diagnostic: "Improve trade efficiency of your firm", action: "Contacter un prestataire"}
];


function collectGroupedScenarioDiagnostics(collector, diagnosticItem/*, idx, list*/) {
  var
    scenarioKey           = diagnosticItem.scenario,
    diagnosticKey         = diagnosticItem.diagnostic,

    groupedDiagnosticKey  = [scenarioKey, diagnosticKey].join(' : '),

    scenarioItem          = collector.scenarioStore[scenarioKey],
    groupedDiagnosticItem = collector.diagnosticStore[groupedDiagnosticKey];

  if (!scenarioItem) {
    scenarioItem          = collector.scenarioStore[scenarioKey] = {

      scenario    : scenarioKey,
      diagnostics : []
    };
    collector.diagnosticsList.push(scenarioItem);
  }
  if (!groupedDiagnosticItem) {
    groupedDiagnosticItem = collector.diagnosticStore[groupedDiagnosticKey] = {

      diagnostic: diagnosticKey,
      actions   : []
    };
    scenarioItem.diagnostics.push(groupedDiagnosticItem);
  }
  groupedDiagnosticItem.actions.push(diagnosticItem.action);

  return collector;
}


var groupedScenarioDiagnostics = diagnosticData.reduce(collectGroupedScenarioDiagnostics, {

  scenarioStore   : {},
  diagnosticStore : {},
  diagnosticsList : []

}).diagnosticsList;


console.log('groupedScenarioDiagnostics : ', groupedScenarioDiagnostics);
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37