2

I am trying to combine/merge 2 array of objects by key in my case id.

Objective:

  1. I am expecting a results where I would have array containing all objects with ids 1,2,3,4 as per example
  2. Order of merging should not affect number of objects in result for example combine(arr1,arr2) or combine(arr2,arr1) should have array with same number of objects
  3. Order of merging can only affect resulting object for example in case of combine(arr1,arr2) arr2 key,values pair can override arr1 key,values just like deep jquery extend $.extend( true, arr1ObJ,arr2ObJ );

JSFIDDLE: https://jsfiddle.net/bababalcksheep/u2c05nyj/

Sample Data:

var arr1 = [{
  id: 1,
  name: "fred",
  title: "boss"
}, {
  id: 2,
  name: "jim",
  title: "nobody"
}, {
  id: 3,
  name: "bob",
  title: "dancer"
}];
var arr2 = [{
  id: 1,
  wage: "300",
  rate: "day"
}, {
  id: 2,
  wage: "10",
  rate: "hour"
}, {
  id: 4,
  wage: "500",
  rate: "week"
}];
var Result = [{
  "id": 1,
  "name": "fred",
  "title": "boss",
  "wage": "300",
  "rate": "day"
}, {
  "id": 2,
  "name": "jim",
  "title": "nobody",
  "wage": "10",
  "rate": "hour"
}, {
  id: 3,
  name: "bob",
  title: "dancer" 
}, {
  id: 4,
  wage: "500",
  rate: "week"
}];
8
  • why you have made it complex. you should use $.extend Commented Jul 30, 2016 at 7:20
  • @Parth $.extend(true,arr1,arr2) skips key with id 3 . see here jsfiddle.net/bababalcksheep/u2c05nyj/2 Commented Jul 30, 2016 at 7:23
  • may this help you underscorejs.org/#union Commented Jul 30, 2016 at 7:42
  • underscore i cant use Commented Jul 30, 2016 at 7:51
  • $.extend won't work because it will think object with id: 3 should be overwritten by object with id: 4 Commented Jul 30, 2016 at 8:02

2 Answers 2

2

Here's a solution. It basically goes through each element of arr2 and checks to see if there's an element with a matching ID arr1. If so, it updates the matching element in arr1 with arr2's values. If there is no match, it simply pushes the element in arr2 onto arr1.

var arr1 = [{id: 1,name: 'fred',title: 'boss'}, 
            {id: 2,name: 'jim',title: 'nobody'}, 
            {id: 3,name: 'bob',title: 'dancer'}];

var arr2 = [{id: 1,wage: '300',rate: 'day'}, 
            {id: 2,wage: '10',rate:'hour'},
            {id: 4,wage: '500',rate: 'week'}];

function combineArrays(arr1, arr2) {
  for(var i = 0; i < arr2.length; i++) {
    // check if current object exists in arr1
    var idIndex = hasID(arr2[i]['id'], arr1);
    if(idIndex >= 0){
      //update
      for(var key in arr2[i]){
        arr1[idIndex][key] = arr2[i][key];
      }
    } else {
      //insert
      arr1.push(arr2[i]);
    }
  }

  return arr1;
}

//Returns position in array that ID exists
function hasID(id, arr) {
  for(var i = 0; i < arr.length; i ++) {
    if(arr[i]['id'] === id)
    {
      return i;
    }
  }

  return -1;
}

var combine = combineArrays(arr1, arr2);
output(combine);

/* pretty Print */
function output(inp) {
  var str = JSON.stringify(inp, undefined, 4);
  $('body').append($('<pre/>').html(str));
}

var arr1 = [{
  id: 1,
  name: 'fred',
  title: 'boss'
}, {
  id: 2,
  name: 'jim',
  title: 'nobody'
}, {
  id: 3,
  name: 'bob',
  title: 'dancer'
}];

var arr2 = [{
  id: 1,
  wage: '300',
  rate: 'day'
}, {
  id: 2,
  wage: '10',
  rate: 'hour'
}, {
  id: 4,
  wage: '500',
  rate: 'week'
}];

function combineArrays(arr1, arr2) {
  for (var i = 0; i < arr2.length; i++) {
    var idIndex = hasID(arr2[i]['id'], arr1);
    if (idIndex >= 0) {
      for (var key in arr2[i]) {
        arr1[idIndex][key] = arr2[i][key];
      }
    } else {
      arr1.push(arr2[i]);
    }
  }

  return arr1;
}

function hasID(id, arr) {
  for (var i = 0; i < arr.length; i++) {
    if (arr[i]['id'] === id) {
      return i;
    }
  }

  return -1;
}

var combine = combineArrays(arr1, arr2);
output(combine);

/* pretty Print */
function output(inp) {
  var str = JSON.stringify(inp, undefined, 4);
  $('body').append($('<pre/>').html(str));
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

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

Comments

1

How about something along the lines of this:

function combineArrays(arr1, arr2, keyFunc) {
    var combined = [],
        keys1 = arr1.map(keyFunc),
        keys2 = arr2.map(keyFunc),
        pos1 = keys1.map(function (id) {
            return keys2.indexOf(id);
        }),
        pos2 = keys2.map(function (id) {
            return keys1.indexOf(id);
        });

    arr1.forEach(function (item, i) {
        combined.push( $.extend(item, arr2[pos1[i]]) );
    });
    arr2.forEach(function (item, i) {
        if (pos2[i] === -1) combined.push( item );
    });
    return combined;    
}

used as

var combine = combineArrays(arr1, arr2, function (item) {
    return item.id;
});

var arr1 = [
    { id: 1, name: 'fred', title: 'boss' },
    { id: 2, name: 'jim', title: 'nobody' },
    { id: 3, name: 'bob', title: 'dancer' }
];
var arr2 = [
    { id: 1, wage: '300', rate: 'day' },
    { id: 2, wage: '10', rate: 'hour' },
    { id: 4, wage: '500', rate: 'week' }
];

function combineArrays(arr1, arr2, keyFunc) {
    var combined = [],
        keys1 = arr1.map(keyFunc),
        keys2 = arr2.map(keyFunc),
        pos1 = keys1.map(function (id) {
            return keys2.indexOf(id);
        }),
        pos2 = keys2.map(function (id) {
            return keys1.indexOf(id);
        });

    arr1.forEach(function (item, i) {
        combined.push( $.extend(item, arr2[pos1[i]]) );
    });
    arr2.forEach(function (item, i) {
        if (pos2[i] === -1) combined.push( item );
    });
    return combined;
}

var combine = combineArrays(arr1, arr2, function (item) {
    return item.id;
});

output(combine);



//
//
//
/* pretty Print */
function output(inp) {
    var str = JSON.stringify(inp, undefined, 4);
    $('body').append($('<pre/>').html(str));
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

4 Comments

It works perfectly. compact code. But I am assuming Michael`s answer stackoverflow.com/a/38672075/2139859 above has only 2 loops ,so it will be more performant ? Please suggest performance wise
Performance is fixed by comparing and analyzing, not by making assumptions. Generally speaking, yes, forEach() is more work than a for loop. But will it matter for your use case? How many array elements do you typically have? Is that section of code under a lot of stress? I wrote "along the lines of this" to imply that you are free to make modifications. Replacing forEach() does not even increase the line count, go ahead and do it if you find that it makes a difference.
Mine actually has 3 loops and should be O(nm + np) if I remember my stuff right. n and m are the sizes of the arrays, p would be the number of properties an object of the array has. If p is always 2 or constant, then it's simply O(nm). If the arrays are always the same length, it's O(n^2). If compactness is important, mine has a lot of unnecessary braces. It's only about 15 lines of code. I wouldn't worry too much about performance unless you're going to run on large input sets. In that case, mine would definitely be a better option.
@Michael Thankx for the input.

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.