0

I am using jQuery $.extend method to do a recursive merge of two objects, but it is not working as I would expect. (Aside: these object literals are in the format required by CKEditor to configure its toolbar.)

Expected Result: Maximize, ShowBlocks, and Source.

Actual Result: Maximize, ShowBlocks.

var obj1 = [
    { name: 'styles', items: ['Format', 'Font', 'FontSize'] },
    { name: 'colors', items: ['TextColor', 'BGColor'] },
    { name: 'tools', items: ['Maximize', 'ShowBlocks'] },
];
var obj2 = [
    { name: 'tools', items: ['Source'] }
];

var recursiveFail = $.extend(true, {}, obj1, obj2);

// Test result here....
alert(recursiveFail[2].items);

Question: Why isn't the 'Source' option being added in?

Jason Parker
  • 4,960
  • 4
  • 41
  • 52

3 Answers3

2

$.extend doesn't work like that: it will copy properties of objects from one object to another. It isn't able to identify things like "I see that 'name' is the same in these objects of this array, so I know to merge the 'items' arrays."

You'll probably have to write your own loop to augment obj1 with obj2.

// Add 'Source' command to the tools group
for (var i = 0; i < obj1.length; i++) {
    if (obj1[i].name === 'tools') {
        obj1[i].items.push('Source');
        break;
    }
}
Jacob
  • 77,566
  • 24
  • 149
  • 228
1

$.extend is not meant to be used this way.

However you can write your own extend/merge function. Here's an example of a function that will merge every objects considered equal (based on the value of a specific key) from multiple provided arrays together.

The following merge algorithm will override primitive values and will concatenate arrays. The initial arrays and their objects aren't modified.

It's probably overkill if you only have to do this once, however it's an example on how you could implement your own encapsulated merging algorithm.

function mergeOnKey(mergeKey) {
    var mergedObjectsIndex = {},
        mergedObjects = [],
        arrays = Array.prototype.slice.call(arguments, 1),
        i = 0,
        arraysLen = arrays.length,
        x, arrLen, arr, obj, k,
        mergeVal, mergedObjIndex,
        mergedObj, mergedVal;

    for (; i < arraysLen ; i++) {
        arr = arrays[i];

        for (x = 0, arrLen = arr.length; x < arrLen; x++) {
            obj = arr[x];
            mergeVal = obj[mergeKey];
            mergedObjIndex = mergedObjectsIndex[mergeVal];

            if (typeof mergedObjIndex === 'undefined') {
                mergedObjectsIndex[mergeVal] = mergedObjects.push($.extend({}, obj)) - 1;
                continue;
            }

            mergedObj = mergedObjects[mergedObjIndex];

            for (k in obj) {
                if (!obj.hasOwnProperty(k) || k === mergeKey) continue;

                if (Array.isArray(mergedVal = mergedObj[k])) mergedObj[k] = mergedVal.concat(obj[k]);
                else mergedObj[k] = obj[k];
            }
        }
    }

    return mergedObjects;
}

You could then use it like:

var obj1 = [
    { name: 'styles', items: ['Format', 'Font', 'FontSize'] },
    { name: 'colors', items: ['TextColor', 'BGColor'] },
    { name: 'tools', items: ['Maximize', 'ShowBlocks'] }
];

var obj2 = [
    { name: 'tools', items: ['Source'], test: 'test' }
];

var merged = mergeOnKey('name', obj1, obj2);

console.log(merged[2].items); //["Maximize", "ShowBlocks", "Source"]
plalx
  • 42,889
  • 6
  • 74
  • 90
0

You can use merge method from lodash library. Check this answer:

https://stackoverflow.com/a/32141113/976556

Community
  • 1
  • 1
Enis
  • 186
  • 1
  • 4