0

I have an array of Javascript objects that look like these:

data = [{PK: "Country1", Prop1: "true", children:[
                {PK: "State1", Prop1: "true", children:[
                        {PK: "City1", Prop1: "Value1"}]
                }]
        },
        {PK: "Country1", Prop2: "true", children:[
                {PK: "State2", Prop2: "true", children:[
                        {PK: "City2", Prop2: "Value2"}]
                }]
        },
        {PK: "Country1", Prop3: "true", children:[
                {PK: "State2", Prop3: "true", children:[
                        {PK: "City3", Prop3: "Value3"}]
                }]
       }]

and I am trying to merge them based on the PK property. Think of them as countries, states, and cities. Currently each object has a country, under its children property is a state and under it is a city. I want them to be merged such that if both objects have the same country, their states will be merged and if both states are the same, their cities will added together in the children property. Then, if a city has a property Prop1, the state should indicate it also has that property, hence the Prop1 = "true". This also follows on the country level. To make it clearer, I am trying to make it look like this:

data = [{PK: "Country1", Prop1: "true", Prop2: "true", Prop3: "true" children:[
                {PK: "State1", Prop1: "true", children:[
                        {PK: "City1", Prop2: "Value1"}]
                },
                {PK: "State2", Prop2: "true", Prop3: "true", children:[
                        {PK: "City2", Prop2: "Value2"},
                        {PK: "City3", Prop3: "Value3"}]
                }]
       }]

I tried something like this but I can't wrap it around the children object which is also an array of objects. Can someone help me on this or lead me to a better answer. Thanks!

Community
  • 1
  • 1
Mark
  • 65
  • 2
  • 7
  • 3
    Please use the term JSON appropriately. These are Javascript objects. JSON is a text interchange format which this is not. – jfriend00 Oct 20 '14 at 23:14
  • 1
    What do you expect to do in the result if multiple objects you are merging have conflicting values for children with the same key. Can children go to any depth or are you just trying to merge the first level of the `children` property? And the result should remove the digits from the `PK` key so `"A1"` becomes `"A"`? A lot more help on how the merge algorithm should work is needed. – jfriend00 Oct 20 '14 at 23:16
  • @jfriend00 - Thanks for pointing that out. Sorry. I edited the Objects to give a clearer picture of the problem. **Think of it as a country with a set of states and each states have a set of cities.** I want to merge all the cities under the same state and all the states under the same country. – Mark Oct 21 '14 at 00:42
  • But, how do you decide which properties on the first level of children to copy up to the top? In your example, you are copying `"PropN"` properties, but not the "PK" property. How to decide what to copy is still unclear. And, in your latest comment, this is now multi-level so you want to do this recursively at more than just one level? – jfriend00 Oct 21 '14 at 00:46
  • @jsfriend00 - If a city has a property **Prop1**, the state of that city should also indicate that **Prop1** exist hence the addition of **Prop1: "true"** in the state level. This also follows in the country level. – Mark Oct 21 '14 at 00:50
  • Yes, this is actually a multi-level merging. – Mark Oct 21 '14 at 00:52
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/63376/discussion-between-mark-and-jfriend00). – Mark Oct 21 '14 at 01:00
  • So, you copy all properties except "PK" up one level and merge the children of items at the same level with the same value of PK and you have to do this recursively to get all levels. Ouch, makes my head hurt. – jfriend00 Oct 21 '14 at 01:09
  • Yes! that is what I'm trying to do but properties are only copied on the same level since in each level of the object it already indicates that it has that property under it. Haha. Made my head hurt also that's why I'm trying to get help here. – Mark Oct 21 '14 at 01:10
  • 1
    I took a shot at it, but didn't quite get there because it turns out that what you want is not a pure recursive merge. I still don't quite understand the rules. I'm out of time to work on it, but here's what I had so far: http://jsfiddle.net/jfriend00/vz718yuL/. It merges the top level correctly, but not the lower levels. – jfriend00 Oct 21 '14 at 02:26
  • @jfriend00 After running your function on the main array. I tried looping your function on the children arrays of the country and state level and then I got my expected result! Haha. I don't know if this okay though or if it can still be simplified. :) – Mark Oct 21 '14 at 22:06

1 Answers1

0

Here is a simple script using a custom merge functionality I've written sometime ago for json-easy-filter Hope it does the right stuff for you. See it in this plunk

var input = ... your data here ...

var groupByPk = function (arr) {
    var res = {};
    arr.forEach(function (node) {
        if (!res[node.PK]) {
            res[node.PK] = [
                node
            ];
        } else {
            res[node.PK].push(node);
        }
    });
    return res;
};

var mergeByPk = function (arr) {
    var groups = groupByPk(arr);
    var groups2 = [];

    for ( var pk in groups) {
        var group = groups[pk];
        var ress = merge.apply(this, group);
        groups2.push(ress.value);
    }
    return groups2;
};

var mergeData = function(data){
    var mergedCountries = mergeByPk(data);
    mergedCountries.forEach(function (country) {
        var mergedStates = mergeByPk(country.children);
        country.children = mergedStates;
        mergedStates.forEach(function (state) {
            var mergedCities = mergeByPk(state.children);
            state.children = mergedCities;
        });
    });
    return mergedCountries;
};

console.log(JSON.stringify(mergeData(input), null, 4));
gliviu
  • 81
  • 4