问题
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.
回答1:
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);
回答2:
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>
回答3:
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);
}, []);
回答4:
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>
回答5:
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"))
)
))
)
))
来源:https://stackoverflow.com/questions/40656890/javascript-merge-two-objects-based-on-key