2

Below are my two arrays .I want to compare them and the resultant array should contain the updated values.Id's are common.. The arrays spans to n levels ie., there is no fixed levels..

The first array ie., the array before updation..

var parentArray1=[
    {
        "id": 1,
        "name": "test",
        "context": [
            {
                "id": 1.1,
                "name": "test 1.1"
            }
        ]
    },
    {
        "id": 2,
        "name": "test"
    },
    {
        "id": 3,
        "name": "test",
        "context": [
            {
                "id": 3.1,
                "name": "test 3.1"
            }
        ]
    },
    {
        "id": 4,
        "name": "test"
    }
]

The operations that i performed are

1.Adding a new Item 2.Updating an existing item

As a result of these two operations the changed values I will be getting in a different array.. ie.,

var changedArray=

       [
        {
            "id": 1,
            "name": "test1",
            "context": [
                {
                    "id": 1.1,
                    "name": "Changed test 1.1"
                }
            ]
        },
        {
            "id": 5,
            "name": "test5"
        }
    ]

Now I have written a generic function that loops through the parentArray1 and using the unique propertiesI need to either add a new item,if the item is there in the changedArray or update an existing item at any level

The resultant array should be ..

[
    {
        "id": 1,
        "name": "test",
        "context": [
            {
                "id": 1.1,
                "name": "Changed test 1.1"
            }
        ]
    },
    {
        "id": 2,
        "name": "test"
    },
    {
        "id": 3,
        "name": "test",
        "context": [
            {
                "id": 3.1,
                "name": "test 3.1"
            }
        ]
    },
    {
        "id": 4,
        "name": "test"
    },
    {
        "id": 5,
        "name": "test5"
    }
]

Generic function:

compareArray(parentArray1, changedArray, ["id"]);

        function compareArray(array1, array2, propertyArray) {
            var newItem = new Array();
            array2.map(function(a1Item) {
                array1.map(function(a2Item) {
                    / If array loop again /
                    if (a2Item.constructor === Array) {
                        compareArray(a2Item, a1Item)
                    } else {
                        / loop the property name to validate /
                        propertyArray.map(function(property) {
                            if (a2Item[property]) {
                                if (a2Item[property] === a1Item[property]) {
                                    a2Item = a1Item
                                } else {
                                    var isAvailable = _.find(newItem, function(item) {
                                        return item[property] === a1Item[property]
                                    })
                                    if (!isAvailable) {
                                        newItem.push(a1Item);
                                    }
                                }
                            }
                        })
                    }

                });
            });

            / Insert the new item into the source array /
            newItem.map(function(item) {
                array1.push(item);
            });
            console.log("After Compare : " + array1);
        }
3
  • The code that you shared has extra functionality in case the item of an array is also an array. Do you want that preserved? Also, let's say that you have something like {a: { b: 'value'}} as an item in array2. If you want to update the value of a in the original object, do you want to update it with =, meaning that you only update the references of the object, or do you want to clone the {b: 'value'} object and have another instance with the same values? Commented Mar 21, 2016 at 10:18
  • In my case the array is nested with different properties and if the value is object then we should clone.The only identifier in the array is the property name ie.,id's Commented Mar 21, 2016 at 10:21
  • I think your question is way too complicated, can't you pinpoint the exact problem you're having? What isn't working like you think it should? Commented Mar 22, 2016 at 8:40

3 Answers 3

1

I suggest to use a temporary object for the reference to the id and update if exist or push if not exist.

var parentArray1 = [{ "id": 1, "name": "test", "context": [{ "id": 1.1, "name": "test 1.1" }] }, { "id": 2, "name": "test" }, { "id": 3, "name": "test", "context": [{ "id": 3.1, "name": "test 3.1" }] }, { "id": 4, "name": "test" }],
    changedArray = [{ "id": 1, "name": "test1", "context": [{ "id": 1.1, "name": "Changed test 1.1" }] }, { "id": 5, "name": "test5" }];

function insert(array, data) {
    function iter(array) {
        array.forEach(function (a) {
            if (!('id' in a)) {
                return;
            }
            if (o[a.id] !== a) {
                o[a.id] = a;
            }
            Object.keys(a).forEach(function (k) {
                Array.isArray(a[k]) && iter(a[k]);
            });
        });
    }

    var o = {};

    iter(array);
    data.forEach(function (a) {
        if (o[a.id]) {
            Object.keys(a).forEach(function (k) {
                o[a.id][k] = a[k];
            });
            return;
        }
        array.push(a);
    });            
}

insert(parentArray1, changedArray);
document.write('<pre>' + JSON.stringify(parentArray1, 0, 4) + '</pre>');

Sign up to request clarification or add additional context in comments.

12 Comments

Thanks for the reply..but why are you using Array.isArray(a.context) && iter(a.context); Since I will not be knowing the property Object names I will be passing the uniqe property nanes ie.,id
that is to build an object with all referenced to the ids in the object, for example if you have id = '1.1', then the object o has the reference to the node with that id, if exist. the functionc is walking the tree, basically.
But I will not be knowing the name "context" and inside context there again can be another object with a different name
that is a problem, if other properties have an array inside.
replace "month": June, with "month": 'June',. for the other question, add the details to the question, or ask another question.
|
0

This is what I came up with:

function sameKeys(o1, o2, keys) {
    for (var i = 0; i < keys.length; i++) {
        var key = keys[i];
        if (!o1.hasOwnProperty(key) || !o2.hasOwnProperty(key))
            throw 'compared objects do not have the key ' + key;
        if (o1[key] !== o2[key])
            return false;
    }
    return true;
}

function isNothing(o) {
    return typeof(o) === 'undefined' || o === null;
}

// this does not work if objects have functions as properties
function clone(o) {
    if (isNothing(o))
        return o;
    return JSON.parse(JSON.stringify(o));
}

function extend(o1, o2, keys) {
    if (isNothing(o2))
        return;
    if (isNothing(o1))
        throw ('first parameter cannot be empty');
    if (typeof(o1) != 'object' || typeof(o2) != 'object')
        throw ('extend only works on objects');
    Object.keys(o2).forEach(function (key) {
        var newVal = o2[key];
        if (o1.hasOwnProperty(key)) {
            if (isNothing(newVal)) {
                delete o1[key];
            } else
                if (Array.isArray(newVal)) {
                    compareArray(o1[key], newVal, keys);
                } else {
                    switch (typeof(newVal)) {
                    case 'object':
                        extend(o1[key], newVal, keys);
                        break;
                    case 'boolean':
                    case 'number':
                    case 'string':
                        o1[key] = newVal;
                        break;
                    default:
                        throw 'not supported property type: ' + typeof(newVal);
                    }
                }
        } else {
            o1[key] = clone(newVal);
        }
    });
}

function removeFromArray(arr, ids, keyArray) {
    var indexes = [];
    var it1s = arr.forEach(function (it, idx) {
            if (sameKeys(ids, it, keyArray)) {
                indexes.push(idx);
            } else {
                Object.keys(it).forEach(function (key) {
                    var newVal = it[key];
                    if (Array.isArray(newVal)) {
                        removeFromArray(it[key], ids, keyArray);
                    }
                });
            }
        });
    if (indexes.length) {
        if (indexes.length > 1)
            throw 'found multiple possible objects for the same key combination'
            arr.splice(indexes[0], 1);
    }
}

function compareArray(a1, a2, keyArray) {

    a2.forEach(function (it2) {
        var it1s = a1.filter(function (it) {
                return sameKeys(it2, it, keyArray);
            });
        var it1;
        if (!it1s.length) {
            it1 = clone(it2);
            a1.push(it1);
        } else {
            if (it1s.length > 1)
                throw 'found multiple possible objects for the same key combination'
                it1 = it1s[0];
            extend(it1, it2, keyArray);
        }
        if (it2.removedIds) {
            it2.removedIds.forEach(function (ids) {
                removeFromArray(a1, ids, keyArray);
            });
        }
    });

}

Use it with compareArray(parentArray1,changedArray,['id']);

Note that it would not work with objects that contain functions. Also, if the arrays would be large, perhaps a better solution is to sort both arrays by key, then always look from the last found object up. That's all I got for now.

Updated it with some concepts from Nina and some clearing of the code.

As I understood it, you only want to add properties. So extend({a: {b: 2}},{a:{c:3}}) will result in {a: {b:2,c:3}}. If this is not what you wanted, let me know.

I also added functionality for removing ids. If any of the objects in the array contains a removedIds array of the form [{id: 4},{id: 5}] then the items with those ids will be removed from the original array.

11 Comments

pls check this plunker plnkr.co/edit/KEZs1dWlJRWpMslC2pZL?p=preview I have added a new activity with activityId 23 Its displaying but what about the existing activity with id 22
Here some of the child objects do not have ids. The sameKeys function finds that two objects without id key have the same key and the algorithm only takes the first one. I am not sure how you want this to work: use any of the keys ending with id? So instead of using all keys in the array, use either of them? I've updated the code to require keys of all compared objects and throw an exception if there are more than one item found for the same key combination.
I have another requirement as if I delete a object in changedArray ie., { "id": 1.1, "name": "Changed test 1.1" } I want to remove that in the parentArray1 after running the compareArray function.How can I do that.Please guide
This is already implemented. The keys you want to delete send them as null. So a=[{id:1,a:1,b:2}];compareArray(a,[{id:1,a:1,b:null}],['id']); will remove the b field
well, if you don't care about the reference to the original array, you can just do this: original=JSON.parse(JSON.stringify(changed)); and you get a clone of the changed array.
|
0

Slight modification on code, to satisfy your conditions. Try it!

function compareArray(originalArray, destinationArray, propertyArray) {
            var newItem = new Array(), processedItem = new Array();
            for (var i = 0; i < originalArray.length; i++) {
                var sourceElement = originalArray[i];

                for (var j = 0; j < destinationArray.length; j++) {
                    var destinationElement = destinationArray[j];
                    var isUpdated = false;

                    if (sourceElement.constructor === Array) {
                        compareArray(sourceElement, destinationElement, propertyArray);
                    } else {
                        /* loop the property name to validate */

                        propertyArray.map(function(property) {
                            if (sourceElement[property]) {
                                if (sourceElement[property] === destinationElement[property]) {
                                    originalArray[i] = _.clone(destinationElement);
                                    isUpdated = true;
                                    return;
                                } else {
                                    var isAvailable = _.find(newItem, function(item) {
                                        return item[property] === destinationElement[property];
                                    });
                                    if (!isAvailable) {
                                        var isAlreadyProcessed = _.find(processedItem, function(item) {
                                            return item[property] === destinationElement[property];
                                        });
                                        if(!isAlreadyProcessed){
                                            newItem.push(destinationElement);
                                        }
                                    }
                                }
                            }
                        });
                    }
                    if (isUpdated === true) {
                        break;
                    }
                }
                processedItem.push(sourceElement);
            }
                newItem.map(function(item) {
                    originalArray.push(item);
                });

                return originalArray;
        }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.