简体   繁体   中英

How do I merge array of Objects but unshift array in JavaScript

I would like to merge these two array of objects but for urls I would like to use unshift to merge arrays instead of replacing.

Here's an example

var arr1 = [{
  "keyword": "name",
  "score": 0.8992112752974006,
  "urls": ["url1"],
  "ids": ["5748bf9ab58adb2f614da195"]
}, {
  "keyword": "name1",
  "score": 0.39953909596222775,
  "urls": ["url2"],
  "ids": ["5743260055f979a31fa98971"]
}, {
  "keyword": "name3",
  "score": 0.4960953181766197,
  "urls": ["url4"],
  "ids": ["58c04cd5208b4945c3920cad"]
}, {
  "keyword": "name4",
  "score": 0.3337163443410707,
  "urls": ["url5"],
  "ids": ["573628c38e32eeb039377f7e"]
}];

var arr2 = [{
  "keyword": "name",
  "score": 0.8992112752974006,
  "urls": ["url6"],
  "ids": [""]
}, {
  "keyword": "name1",
  "score": 0.39953909596222775,
  "urls": ["url7"],
  "ids": [""]
}]

I would like the result to be

[{
  "keyword": "name",
  "score": 0.8992112752974006,
  "urls": ["url6", "url1"],
  "ids": ["5748bf9ab58adb2f614da195"]
}, {
  "keyword": "name1",
  "score": 0.39953909596222775,
  "urls": ["url7", "url2"],
  "ids": ["5743260055f979a31fa98971"]
}, {
  "keyword": "name3",
  "score": 0.4960953181766197,
  "urls": ["url4"],
  "ids": ["58c04cd5208b4945c3920cad"]
}, {
  "keyword": "name4",
  "score": 0.3337163443410707,
  "urls": ["url5"],
  "ids": ["573628c38e32eeb039377f7e"]
}];    

Here's my attempt but the result would replace the array of urls;

var a3 = arr1.concat(arr2).reduce((acc, x) => {
  acc[x.keyword] = Object.assign(acc[x.keyword] || {}, x);
  return acc;
}, {});
console.log(a3);

It would be just the urls that it will be merged. Other values will be overridden.

You could use a Map for collecting the objects with the same keyword and update if necessary. This solution works for an arbitrary count of arrays.

 var array1 = [{ keyword: "name", score: 0.8992112752974006, urls: ["url1"], ids: ["5748bf9ab58adb2f614da195"] }, { keyword: "name1", score: 0.39953909596222775, urls: ["url2"], ids: ["5743260055f979a31fa98971"] }, { keyword: "name3", score: 0.4960953181766197, urls: ["url4"], ids: ["58c04cd5208b4945c3920cad"] }, { keyword: "name4", score: 0.3337163443410707, urls: ["url5"], ids: ["573628c38e32eeb039377f7e"] }], array2 = [{ keyword: "name", score: 0.8992112752974006, urls: ["url6"], ids: [""] }, { keyword: "name1", score: 0.39953909596222775, urls: ["url7"], ids: [""] }], map = new Map, result = [], fn = a => { if (map.has(a.keyword)) { map.get(a.keyword).urls.unshift(...a.urls); return; } map.set(a.keyword, a); result.push(a); }; [array1, array2].forEach(a => a.forEach(fn)); console.log(result); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

I prefer doing this with a Map instead of reduce:

 const result = [], hash = new Map();

 arr1.concat(arr2).forEach( obj => {
   if( hash.has( obj.keyword ) ){
    hash.get( obj.keyword ).urls.push(...obj.urls);
   }else{
    hash.set( obj.keyword, obj);
    result.push( obj );
  }
 });

You can take the urls property out of the object before you assign to a new object, then add it back if it doesn't exist, or unshift into the existing array, if it does exist

 var arr1 = [{ "keyword": "name", "score": 0.8992112752974006, "urls": ["url1"], "ids": ["5748bf9ab58adb2f614da195"] }, { "keyword": "name1", "score": 0.39953909596222775, "urls": ["url2"], "ids": ["5743260055f979a31fa98971"] }, { "keyword": "name3", "score": 0.4960953181766197, "urls": ["url4"], "ids": ["58c04cd5208b4945c3920cad"] }, { "keyword": "name4", "score": 0.3337163443410707, "urls": ["url5"], "ids": ["573628c38e32eeb039377f7e"] }]; var arr2 = [{ "keyword": "name", "score": 0.8992112752974006, "urls": ["url6"], "ids": [""] }, { "keyword": "name1", "score": 0.39953909596222775, "urls": ["url7"], "ids": [""] }] var a3 = arr1.concat(arr2).reduce((acc, x) => { let urls = x.urls; // store temporarily delete x.urls; // delete before merging, to not overwrite acc[x.keyword] = Object.assign(acc[x.keyword] || {}, x); 'urls' in acc[x.keyword] ? [].unshift.apply(acc[x.keyword].urls, urls) : acc[x.keyword].urls = urls; // ^ if exists unshift or just add return acc; }, {}); console.log(a3); 

For no side effects on arr1 or arr2 you'll also need some kind of clone.

const a3 = ((a1, a2) => {
    const copy = item => {
        return {
            keyword: item.keyword,
            score: item.score,
            urls: item.urls.slice(),
            ids: item.ids.slice()
        };
    };

    a1 = a1.map(copy);

    const dictionary = a1.reduce((acc, item) => {
        acc[item.keyword] = item;
        return acc;
    }, {});

    a2.forEach(item => {
        if (dictionary[item.keyword]) {
            dictionary[item.keyword].urls = item.urls.concat(dictionary[item.keyword].urls);
        } else {
            const clone = copy(item);
            dictionary[clone.keyword] = clone;
            a1.push(clone);
        }
    });
    return a1;
})(arr1, arr2);

I wrote this using arrows as function shorthand/ const , but the rest is ES5

I used a spread operator to merge the arrays but unshift() would work just as well.

 function mergeObjects(arrayA, arrayB) { var // Take all items in arrayA and place them in the result. result = arrayA.slice(0); // Iterate over all items in array B arrayB.forEach(item => { var // Check if there is an item in the result with the same keyword. resultItem = result.find(itemA => itemA.keyword === item.keyword); // When the keyword isn't yet in the result... if (resultItem === null) { // ... add it to the result result.push(item); } else { // ... or else add the urls from the current item to the urls of the result. resultItem.urls = [...item.urls, ...resultItem.urls]; } }); // Return the result array. return result; } var arr1 = [{ "keyword": "name", "score": 0.8992112752974006, "urls": ["url1"], "ids": ["5748bf9ab58adb2f614da195"] }, { "keyword": "name1", "score": 0.39953909596222775, "urls": ["url2"], "ids": ["5743260055f979a31fa98971"] }, { "keyword": "name3", "score": 0.4960953181766197, "urls": ["url4"], "ids": ["58c04cd5208b4945c3920cad"] }, { "keyword": "name4", "score": 0.3337163443410707, "urls": ["url5"], "ids": ["573628c38e32eeb039377f7e"] }]; var arr2 = [{ "keyword": "name", "score": 0.8992112752974006, "urls": ["url6"], "ids": [""] }, { "keyword": "name1", "score": 0.39953909596222775, "urls": ["url7"], "ids": [""] }]; console.log(mergeObjects(arr1, arr2)); 

ES5 optimized approach ( without unnecessary iterations , just " low-level " processing):

 var arr1 = [{ keyword: "name", score: 0.8992112752974006, urls: ["url1"], ids: ["5748bf9ab58adb2f614da195"] }, { keyword: "name1", score: 0.39953909596222775, urls: ["url2"], ids: ["5743260055f979a31fa98971"] }, { keyword: "name3", score: 0.4960953181766197, urls: ["url4"], ids: ["58c04cd5208b4945c3920cad"] }, { keyword: "name4", score: 0.3337163443410707, urls: ["url5"], ids: ["573628c38e32eeb039377f7e"] }], arr2 = [{ keyword: "name", score: 0.8992112752974006, urls: ["url6"], ids: [""] }, { keyword: "name1", score: 0.39953909596222775, urls: ["url7"], ids: [""] }], len1 = arr1.length, c = 0, k = -1; while (c < len1 && (l = arr2.length)) { for (i=0; i<l; i++) { if (arr1[c].keyword == arr2[i].keyword) { arr1[c].urls.unshift(arr2[i].urls[0]); k = i; break; } } if (k >=0) { arr2.splice(k, 1); k = -1; } c++; } console.log(arr1); 


Performance comparison results ( https://jsperf.com/map-vs-reduce-vs-while-loop ):

在此处输入图片说明

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