简体   繁体   中英

Merging objects in array by matching same key values

Sorry if the json is not perfect, i was trying to type it into the Stackoverflow window... Anyway, you can see from below that I want to take all of the separate objects in the array and merge them into the first array that has the matching url. Once they are merged all of the others need to get removed from the array.

var myArray = [
{ 
 id: '123',
 url: 'http://foo.com/1.jpg',
 size: 7,
 qty: 1
},
{
 id: '345',
 url: 'http://foo.com/5.jpg',
 color: 'blue',
 qty: 5
},
{
 id: '678',
 url: 'http://foo.com/1.jpg',
 size: 8,
 qty: 4
}];

I need to make this array turn into... Below you can see that the objects that had matching url now have been moved into the first matched objects 'variations' key. They no longer appear separate after that. They are essentially all matched together and merged into the same object.

var myArray = [{ 
 id: '123',
 url: 'http://foo.com/1.jpg',
 variations:[
     {
       id : '123'
       size : 7,
       qty: 1
     }, 
     {
       id : '678'
       size : 8,
       qty: 4
     }],
{
 id: '345',
 url: 'http://foo.com/5.jpg',
 color: 'blue',
 qty: 5
}];

So far I have something like this: But this is just work in progress. Having trouble bringing this to the finish line.

myArray.forEach(function(product, index){
  var sizeVariations = [];
  var currentSearch = product.url;
  var filteredArray = processedProducts.filter(function( obj ) {
    return obj.primary_image === currentSearch;
  });

  if(filteredArray){
    filteredArray.forEach(function(data, index){
      sizeVariations.push({
        id : data.id,
        size : data.size,
        quantity : data.qty
      });
    });
  }
});

You can do something similar to this; I am not keeping the id at the top level since it looks like a duplication of data.

 var myArray = [ { id: '123', url: 'http://foo.com/1.jpg', size: 7, qty: 1 }, { id: '345', url: 'http://foo.com/5.jpg', color: 'blue', qty: 5 }, { id: '678', url: 'http://foo.com/1.jpg', size: 8, qty: 4 }]; function mergeObjects(arr) { var resultArray = []; var urls = []; for(var item in arr) { var itemIndex = urls.indexOf(arr[item].url); if(itemIndex == -1) { urls.push(arr[item].url); var obj = {}; obj.url = arr[item].url; obj.variations = []; var variationData = {}; variationData.id = arr[item].id; if(arr[item].size !== undefined) { variationData.size = arr[item].size; } if(arr[item].qty !== undefined) { variationData.qty = arr[item].qty; } if(arr[item].color !== undefined) { variationData.color = arr[item].color; } obj.variations.push(variationData); resultArray.push(obj); } else { var variationData = {}; variationData.id = arr[item].id; if(arr[item].size !== undefined) { variationData.size = arr[item].size; } if(arr[item].qty !== undefined) { variationData.qty = arr[item].qty; } if(arr[item].color !== undefined) { variationData.color = arr[item].color; } resultArray[itemIndex].variations.push(variationData) } } return resultArray; } console.log(mergeObjects(myArray)); 

I think you can try to gather all the urls in an object first so that you can guarantee uniqueness and then convert it to an array, something like this,

function Merge(arr) {
    var obj = {};

    for (var elem of arr) {
        if (obj[elem.url] == undefined) {
            obj[elem.url] = {
                id: elem.id,
                variations: [],
                url: elem.url
            };
        }

        obj[elem.url].variations.push(elem);
        delete elem.url;
    }

    var ans = [];
    for (var elem in obj) {
        var tmp = {
            id: obj[elem].id,
            url: elem
        };
        if(obj[elem].variations.length > 1) 
            tmp.variations = obj[elem].variations;
        else {
            tmp.color = obj[elem].variations[0].color;
            tmp.qty = obj[elem].variations[0].qty;
        }
        ans.push(tmp);
    }

    return ans;
}

I feel like a much better data structure would be one keyed by url .

 const myArray = [{"id":"123","url":"http://foo.com/1.jpg","size":7,"qty":1},{"id":"345","url":"http://foo.com/5.jpg","color":"blue","qty":5},{"id":"678","url":"http://foo.com/1.jpg","size":8,"qty":4}] const urlMap = myArray.reduce((byUrl, entry) => { const entryWithoutUrl = Object.assign({}, entry) delete entryWithoutUrl.url if (Array.isArray(byUrl[entry.url])) { byUrl[entry.url].push(entryWithoutUrl) } else { byUrl[entry.url] = [entryWithoutUrl] } return byUrl }, Object.create(null)) const newArray = Object.keys(urlMap).map(url => ({ url: url, variations: urlMap[url] })) console.info(newArray) 

In this example I have broken the problem down into two smaller problems for efficiency of not having to iterate through the array to check if a url already exists, then once everything is grouped by the url, we convert the resulting object back to an array with each key as an item in the array.

if you use functional programming you could compose these functions together

compose(objectToArray, sortByUnique)('url', myArray)

 var myArray = [ { id: '123', url: 'http://foo.com/1.jpg', qty: 1, size: 7 }, { id: '345', url: 'http://foo.com/5.jpg', qty: 5, color: 'blue' }, { id: '678', url: 'http://foo.com/1.jpg', qty: 4, size: 8 }, { id: '69', url: 'http://foo.com/1.jpg', qty: 4, size: 8 } ] function sortByUnique(unique, array) { // group array by unique key into an object return array.reduce((acc, x) => { let key = x[unique] delete x[unique] if (acc[key]) { if (!acc[key].variations) { let old = acc[key] acc[key] = { variations: [old, x] } } else { acc[key].variations.push(x) } } else { acc[key] = x } return acc }, {}) } function objectToArray(obj) { return Object.keys(obj).map( k => Object.assign( { url: k }, obj[k] )) } console.log( objectToArray(sortByUnique('url', myArray)) ) 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM