简体   繁体   中英

Javascript merge two objects based on key

I have some objects in the shape of below.

[{
    product: 'ABC',
    productId: 'AB123',
    batch: 'BA1',
    price: '12'
}, {
    product: 'ABC',
    productId: 'AB123',
    batch: 'BA2',
    price: '15'
}, {
    product: 'XYZ',
    productId: 'AB124',
    batch: 'XY1',
    price: '124'
}]

I want to merge objects into one single object in the array if key pair ( product , and productId ) are mathced, in the below format.

[{
    product: 'ABC',
    productId: 'AB123',
    batch: ['BA1', 'BA2'],
    price: ['12', '15']
}, {
    product: 'XYZ',
    productId: 'AB124',
    batch: 'XY1',
    price: '124'
}]

How can I do it in lodash or in pure javascript.

This proposal does not alter the given data.

It creates new objects, first with just single data, later if more data should be grouped, it uses an array for batch and price .

 var data = [{ product: 'ABC', productId: 'AB123', batch: 'BA1', price: '12' }, { product: 'ABC', productId: 'AB123', batch: 'BA2', price: '15' }, { product: 'XYZ', productId: 'AB124', batch: 'XY1', price: '124'}], merged = []; data.forEach(function (a) { if (!this[a.productId]) { this[a.productId] = { product: a.product, productId: a.productId, batch: a.batch, price: a.price }; merged.push(this[a.productId]); return; } if (!Array.isArray(this[a.productId].batch)) { this[a.productId].batch = [this[a.productId].batch]; } if (!Array.isArray(this[a.productId].price)) { this[a.productId].price = [this[a.productId].price]; } this[a.productId].batch.push(a.batch); this[a.productId].price.push(a.price); }, Object.create(null)); console.log(merged); 

You can merge similar objects in the array using a lodash's chain with _.transform() and _.mergeWith() :

 function mergeSimilar(arr, arrayProps) { // transform the array into a map object return _(arr).transform(function(result, item) { // create a temp id that includes the product and productId var id = item.product + item.productId; // merge the existing item with a new item result[id] = _.mergeWith(result[id] || {}, item, function(objValue, srcValue, key) { // if a value exists, and it's one of the request keys, concat them into a new array if (!_.isUndefined(objValue) && _.includes(arrayProps, key)) { return [].concat(objValue, srcValue); } }); }, {}) .values() // get the values from the map object .value(); } var arr = [{ product: 'ABC', productId: 'AB123', batch: 'BA1', price: '12' }, { product: 'ABC', productId: 'AB123', batch: 'BA2', price: '15' }, { product: 'XYZ', productId: 'AB124', batch: 'XY1', price: '124' }]; var result = mergeSimilar(arr, ['batch', 'price']); console.log(result); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script> 

You can make use of _.uniqWith to loop over the collection and get rid of duplicates. Apart from that, uniqWith grants you access to the objects themselves so you can tamper them as you like.

In this case, when a duplicate is found, I add its batch and price to the array of the original object, getting the desired result.

 var arr = [{ product: 'ABC', productId: 'AB123', batch: 'BA1', price: '12' }, { product: 'ABC', productId: 'AB123', batch: 'BA2', price: '15' }, { product: 'XYZ', productId: 'AB124', batch: 'XY1', price: '124' }]; function addToArray(val1, val2) { return _.isArray(val1) ? val1.concat(val2) : [val1].concat(val2); } function modifyObjs(a, b) { b.batch = addToArray(b.batch, a.batch); b.price = addToArray(b.price, a.price); return true; } function predicateAndModifier(a, b) { return a.product === b.product && a.productId === b.productId && modifyObjs(a, b); } console.log(_.uniqWith(arr, predicateAndModifier)); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script> 

Lodash 4.17.2

_.reduce(data, function(result, item) {
    var added = _.find(result, {
        product: item.product,
        productId: item.productId
    });
    if (_.isObject(added)) {
        //!! better to merge with new object and add result to array again to avoid mutable
        added = _.mergeWith(added, item, function(addedVal, itemVal, key) {
            if (key === 'product' || key === 'productId') {
                return addedVal;
            }
            return _.concat(addedVal, itemVal);
        });
        return result;
    }
    return _.concat(result, item);
}, []);

Does it have to be readable?

var data = [{
    product: 'ABC',
    productId: 'AB123',
    batch: 'BA1',
    price: '12'
}, {
    product: 'ABC',
    productId: 'AB123',
    batch: 'BA2',
    price: '15'
}, {
    product: 'ABC',
    productId: 'AB113',
    batch: 'BA2',
    price: 15
}, {
    product: 'XYZ',
    productId: 'AB124',
    batch: 'XY1',
    price: '124'
}]
var unEs6 = function(x) {
    if (x instanceof Map) {
        var result = {}
        for (let [key, value] of x.entries()) {
            result[key] = unEs6(value);
        }
        return result;
    }
    else {
        return x
    }
}
JSON.stringify(unEs6(
    data
        .map(
            row => (new Map().set(
                row.product, new Map().set(
                    row.productId, new Map()
                        .set("batch", [row.batch])
                        .set("price", [row.price])
                    )
                )
            )
        )
        .reduce((a, b) => !a.has(b.keys().next().value) ?
            new Map([...a, ...b]) :
            !a.get(b.keys().next().value).has(b.get(b.keys().next().value).keys().next().value) ?
                a.set(b.keys().next().value, new Map([
                    ...a.get(b.keys().next().value),
                    ...b.get(b.keys().next().value)
                ])) :
                a.set(b.keys().next().value, a.get(b.keys().next().value).set(
                    b.get(b.keys().next().value).keys().next().value,
                    new Map()
                        .set("batch", a.get(b.keys().next().value).get(b.get(b.keys().next().value).keys().next().value).get("batch")
                              .concat(b.get(b.keys().next().value).get(b.get(b.keys().next().value).keys().next().value).get("batch"))
                        )
                        .set("price", a.get(b.keys().next().value).get(b.get(b.keys().next().value).keys().next().value).get("price")
                              .concat(b.get(b.keys().next().value).get(b.get(b.keys().next().value).keys().next().value).get("price"))
                        )
                ))
        )
))

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