lodash json group object costs - javascript

Please,
I have this JSON object and want to group values by type.
var costs = [
{ 'name': 'JON', 'flight':100, 'value': 12, type: 'uns' },
{ 'name': 'JON', 'flight':100, 'value': 35, type: 'sch' },
{ 'name': 'BILL', 'flight':200, 'value': 33, type: 'uns' },
{ 'name': 'BILL', 'flight':200, 'value': 45, type: 'sch' }
];
I want something like this:
var costs = [
{ 'name': 'JON', 'flight':100, 'uns': 12, 'sch': 35 },
{ 'name': 'BILL', 'flight':200, 'uns': 33, 'sch': 45}
];
I try use lodash but without sucess:
var compiled_costs = _.chain(costs)
.groupBy("flight")
.value();
{
"100":
[ {"name":"JON","flight":100,"value":12,"type":"uns"},
{"name":"JON","flight":100,"value":35,"type":"sch"}
],
"200":
[
{"name":"BILL","flight":200,"value":33,"type":"uns"},
{"name":"BILL","flight":200,"value":45,"type":"sch"}
]
}

var res = _.chain(costs)
.groupBy('flight') // group costs by flight
.mapValues(function(flightItems, flight) { // iterate flight arrays
return { // return obj on each flight array
name: _.get(flightItems, [0, 'name']), // just get name from first item of flight array
flight: flight,
uns: _.chain(flightItems) // get all flight items with type uns and sum items values
.filter({type: 'uns'})
.sumBy('value')
.value(),
sch: _.chain(flightItems)
.filter({type: 'sch'})
.sumBy('value')
.value()
}
})
.values() // get values from object
.value();

You can use native reduce() method:
const costs = [
{ 'name': 'JON', 'flight':100, 'value': 12, type: 'uns' },
{ 'name': 'JON', 'flight':100, 'value': 35, type: 'sch' },
{ 'name': 'BILL', 'flight':200, 'value': 33, type: 'uns' },
{ 'name': 'BILL', 'flight':200, 'value': 45, type: 'sch' }
];
const compiledCosts = costs.reduce((acc, { name, flight, value, type }) => {
let obj = acc.find(x => x.name === name);
if (typeof obj === 'undefined') {
acc.push({ name, flight, [type]: value });
} else {
obj[type] = value;
}
return acc;
}, []);
console.log(compiledCosts);

You could use a closure over a hash table for same named objects.
var data = [{ name: 'JON', flight: 100, value: 12, type: 'uns' }, { name: 'JON', flight: 100, value: 35, type: 'sch' }, { name: 'BILL', flight: 200, value: 33, type: 'uns' }, { name: 'BILL', flight: 200, value: 45, type: 'sch' }],
grouped = data.reduce(function (hash) {
return function (r, o) {
if (!hash[o.name]) {
hash[o.name] = { name: o.name, flight: o.flight };
r.push(hash[o.name]);
}
hash[o.name][o.type] = (hash[o.name][o.type] || 0) + o.value;
return r;
}
}(Object.create(null)), []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

This is what Map was made for. Since you are merging the data you might want to consider keeping the hashMap for quicker lookup. Otherwise turning it into an array is trivial as I demonstrate.
function compiled_costs(costs) {
var hash = new Map();
for (let cost of costs) {
if (hash.has(cost.name)) {
var values = hash.get(cost.name),
value = values.value,
type = values.type;
delete values.value;
delete values.type;
values.uns = value;
values.sch = cost.value;
} else hash.set(cost.name, cost)
}
return hash
}
var costs = [{
'name': 'JON',
'flight': 100,
'value': 12,
type: 'uns'
},
{
'name': 'JON',
'flight': 100,
'value': 35,
type: 'sch'
},
{
'name': 'BILL',
'flight': 200,
'value': 33,
type: 'uns'
},
{
'name': 'BILL',
'flight': 200,
'value': 45,
type: 'sch'
}
];
var formated = [...compiled_costs(costs).values()];
//formated
console.log('formated',formated);
//hashed
var hashed = [...compiled_costs(costs)];
console.log('hashed',hashed);

Related

Multiple dynamic filter with nested level array in javascript

Input :
// First Array
const input1 = [{
'name': "name1",
'email': "email1#email.com",
'age': 10,
'score':95,
'address': {
'city': "city1"
}
},
{
'name': "name2",
'email': "email2#email.com",
'age': 10,
'score':45,
'address': {
'city': "city2"
}
}
];
// Second Array
const input2 = [{
'id': 1,
'fullname': "name1",
'emailaddress': "email1#email.com",
'age': 10,
'score':45,
'address': {
'city': "city1"
}
},
{
'id': 5,
'name': "name2",
'email': "email2#email.com",
'age': 20,
'score':55,
'address': {
'city': "city2"
}
}
];
const filter1 = [{
"filter1Key": "age",
"filter2Key": "score"
}];
const filter2 = [{
"filter1Key": "name",
"filter2Key": "address.city"
}];
const newArray = [];
cont updateArray = [];
//Below code is not is giving
const test1 = input1.filter((data) => input2.some((obj) =>
filter1.every(key => data[key.filter1Key] === obj[key.filter2Key])?filter2.every(key => data[key.filter1Key] === obj[key.filter2Key])?'':updateArray.push(obj):newArray.push(obj)));
console.log(test1);
First all the unmatched record with filter1 of input1 should be push into newArray and unmatched record with filter2 of input1 should be push into updateArray but id of inout2 should also push with that record
Expected output:
newArray = [{
'name': "name1",
'email': "email1#email.com",
'age': 10,
'score':95,
'address': {
'city': "city1"
}
}];
updateArray = [{
'id': 5,
'name': "name2",
'email': "email2#email.com",
'age': 10,
'score':45,
'address': {
'city': "city2"
}
}]
Convert the first ? to && and the second ? to == and the first : to ?
you may change below code by your es6+ syntax--
I wish It might help you
input1.forEach((input1Item) => {
const x = input2.find((input2Item) =>{
return input1Item[filter1[0].filter1Key] === input2Item[filter1[0].filter1Key] && input1Item[filter1[0].filter2Key] === input2Item[filter1[0].filter2Key]
})
const y = input2.find((input2Item) =>{
return input1Item[filter1[0].filter1Key] !== input2Item[filter1[0].filter1Key] || input1Item[filter1[0].filter2Key] !== input2Item[filter1[0].filter2Key]
})
if(!x){
newArray.push(input1Item)
} else if(y){
updateArray.push({ id: y.id , ...input1Item})
}
});

Group array of objects by 1 key, and split 1 attribute

I have this array of objects
test = [
{
'id': 1,
'name': 'XYZ'
'value': 10
'quantity': 100
},
{
'id': 1,
'name': 'XYZ'
'value': 20
'quantity': 200
},
{
'id': 2,
'name': 'ABC'
'value': 11
'quantity': 111
},
{
'id': 2,
'name': 'ABC'
'value': 22
'quantity': 222
}
]
And I want to group them by the id, but with the name and the {value, quantity} separated, like this:
result = {
1: [
'name': 'XYZ'
'items': [
{
'value': 10
'quantity': 100
},
{
'value': 20
'quantity': 200
}
]
],
2: [
'name': 'ABC'
'items': [
{
'value': 11
'quantity': 111
},
{
'value': 22
'quantity': 222
}
]
],
}
Any idea how I could do this? Grouping by the id I can do, but then I cannot extract the name.
Thanks
I think you can use reduce() in this case to group the elements by id
const test = [
{
'id': 1,
'name': 'XYZ',
'value': 10,
'quantity': 100,
},
{
'id': 1,
'name': 'XYZ',
'value': 20,
'quantity': 200,
},
{
'id': 2,
'name': 'ABC',
'value': 11,
'quantity': 111,
},
{
'id': 2,
'name': 'ABC',
'value': 22,
'quantity': 222,
}
];
const res = test.reduce((ac, {id, name, ...rest}) => ({...ac, [id]: ac[id] ? [...ac[id], rest] : [rest] }), {});
console.log(res)
You can easily achieve this result using reduce
const arr = [
{
id: 1,
name: "XYZ",
value: 10,
quantity: 100,
},
{
id: 1,
name: "XYZ",
value: 20,
quantity: 200,
},
{
id: 2,
name: "ABC",
value: 11,
quantity: 111,
},
{
id: 2,
name: "ABC",
value: 22,
quantity: 222,
},
];
const result = arr.reduce((acc, curr) => {
const { id, name, value, quantity } = curr;
if (acc[id]) {
acc[id].items.push({ value, quantity });
} else {
acc[id] = {
name,
items: [{ value, quantity }],
};
}
return acc;
}, {});
console.log(result);
You can use foreach in order to get what you need:
test = [
{
id: 1,
name: 'XYZ',
value: 10,
quantity: 100,
},
{
id: 1,
name: 'XYZ',
value: 20,
quantity: 200,
},
{
id: 2,
name: 'ABC',
value: 11,
quantity: 111,
},
{
id: 2,
name: 'ABC',
value: 22,
quantity: 222,
}
];
res = {};
test.forEach(el => {
let key = el.id;
if (!res.hasOwnProperty(key)) {
res[key] = {
name: el.name,
items: []
};
}
res[key].items.push({value: el.value, quantity: el.quantity});
});
console.log(res);
You could take a destructuring for id and name and get the rest of the object (Rest in Object Destructuring ...) as item.
Then assign a new object if not exists for the property id and push item to the items array of the group.
const
data = [{ id: 1, name: 'XYZ', value: 10, quantity: 100 }, { id: 1, name: 'XYZ', value: 20, quantity: 200 }, { id: 2, name: 'ABC', value: 11, quantity: 111 }, { id: 2, name: 'ABC', value: 22, quantity: 222 }],
result = data.reduce((r, { id, name, ...item }) => {
r[id] ??= { name, items: [] };
r[id].items.push(item);
return r;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to group an array of objects by key and sums a nested object property

I have an array of product objects, each product contains the product price besides a nested object representing the currency used.
var products = [
{
'id': '1',
'currency': { id:1, name:'Dollar' },
'price': '100'
}, {
'id': '4',
'currency': {
id:2,
name:'Euro',
current_quotation: {
date: '2020-03-02',
quotation: 68
}
},
'price': '300'
}, {
'id': '6',
'currency': { id:1, name:'Dollar' },
'price': '50'
}, {
'id': '16',
'currency': null,
'price': '50'
}, {
'id': '23',
'currency': { id:2, name:'Euro' },
'price': null
}
];
What I'm trying to do is to get an array with a unique object per currency with its price sum, preferably using lodash or ES6 script. What I need as a result is:
var subtotals = [
{currency:'Dollar', total: 150},
{currency:'Euro', total: 300}
]
I tried with many samples but no luck so far. My approach is this, but it's returning the whole product object within the array, which is so far from expected result
let subtotals = state.products.reduce((h, product) => Object.assign(h, { [product.currency?.name]:( h[product.currency?.name] || [] ).concat({currency: product.currency?.name, price: product.price}) }), {})
Result
{
"Euro":[
{"currency":"Euro","price":"300"}
],
"Dolar":[
{"currency":"Dolar","price":"100"},
{"currency":"Dolar","price":"50"}
]
}
Please note that some products may have no currency at the begining since it value comes from a dropdown input. In that case the value can be ommited in the result.
Any help will be appreciated.
Assuming that you want to sum up the totals and get a Euro total of 300:
Iterate over each currency.name and price, transforming the input into an object whose keys are the currency names and values are the cumulative price for that currency name found so far. At the end, map the object's entries to get an array of objects of your desired format:
var products = [
{
'id': '1',
'currency': { id:1, name:'Dollar' },
'price': '100'
}, {
'id': '4',
'currency': { id:2, name:'Euro' },
'price': '300'
}, {
'id': '6',
'currency': { id:1, name:'Dollar' },
'price': '50'
}
];
const pricesByName = {};
for (const { currency: { name }, price } of products) {
pricesByName[name] = (pricesByName[name] || 0) + Number(price);
}
const output = Object.entries(pricesByName)
.map(([currency, total]) => ({ currency, total }));
console.log(output);
If the currency or price is null, ignore them using an if statement:
var products = [
{
'id': '1',
'currency': { id:1, name:'Dollar' },
'price': '100'
}, {
'id': '4',
'currency': { id:2, name:'Euro' },
'price': '300'
}, {
'id': '6',
'currency': { id:1, name:'Dollar' },
'price': '50'
}, {
'id': '16',
'currency': null,
'price': '50'
}, {
'id': '23',
'currency': { id:2, name:'Euro' },
'price': null
}
];
const pricesByName = {};
for (const { currency, price } of products) {
if (price === null || currency === null) continue;
const { name } = currency;
pricesByName[name] = (pricesByName[name] || 0) + Number(price);
}
const output = Object.entries(pricesByName)
.map(([currency, total]) => ({ currency, total }));
console.log(output);
Using lodash, you can group by the currency object's name property using _.groupBy(). And then map the result object to an array of objects. The object which you map to can use the key as the currency and the summed total of the grouped array (using _.sumBy()) as the total:
const products = [ { 'id': '1', 'currency': { id:1, name:'Dollar' }, 'price': '100' }, { 'id': '4', 'currency': { id:2, name:'Euro' }, 'price': '300' }, { 'id': '6', 'currency': { id:1, name:'Dollar' }, 'price': '50' }, { 'id': '16', 'currency': null, 'price': '50' }, { 'id': '23', 'currency': { id:2, name:'Euro' }, 'price': null } ];
const getSubTotals = p => _.flow(
products => _.filter(products, o => _.has(o, p)),
sm_prod => _.groupBy(sm_prod, p),
gr => _.map(gr, (arr, currency) => ({currency, total: _.sumBy(arr, ({price}) => +price)}))
);
console.log(getSubTotals('currency.name')(products));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
EDIT As you have changed your question. You'll need to use some alternate code to get your desired result:
const products = [ { 'id': '1', 'currency': { id:1, name:'Dollar' }, 'price': '100' }, { 'id': '4', 'currency': { id:2, name:'Euro', current_quotation: { date: '2020-03-02', quotation: 68 } }, 'price': '300' }, { 'id': '6', 'currency': { id:1, name:'Dollar' }, 'price': '50' }, { 'id': '16', 'currency': null, 'price': '50' }, { 'id': '23', 'currency': { id:2, name:'Euro' }, 'price': null } ];
const getSubTotals = p => products => {
const _gr = _(products).filter(o => _.has(o, p)).groupBy(p);
const quote_map = _gr.mapValues((arr, k) => _.sumBy(arr, o => _.get(o, 'currency.current_quotation.quotation', 0)) || 1).value();
const _totals = _gr.map((arr, currency) => ({currency, total: _.sumBy(arr, ({price}) => +price)}));
return _totals.map(({currency, total, ...r}) => ({currency, total, quote_total: total*quote_map[currency]})).value();
}
console.log(getSubTotals('currency.name')(products));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
I wrote this up in the console and it should work for your case:
var products = [
{
id: '1',
currency: { id: 1, name: 'Dollar' },
price: '100',
},
{
id: '4',
currency: { id: 2, name: 'Euro' },
price: '300',
},
{
id: '6',
currency: { id: 1, name: 'Dollar' },
price: '50',
},
];
subtotals = {};
products.map(x => {
if(x.currency !== null){
subtotals[x.currency.name] === undefined
? (subtotals[x.currency.name] = parseInt(x.price))
: (subtotals[x.currency.name] += parseInt(x.price));
}
});

How to return an object which has difference between two array object using lodash

for example
let array1 = [
{ 'id': 1010, 'name': 'grapes' },
{ 'id': 2020, 'name': 'blueberry' },
{ 'id': 3030, 'name': 'banana' }
]
let array2 = [
{ 'id': 1010, 'name': 'apple' },
{ 'id': 2020, 'name': 'blueberry' },
{ 'id': 4040, 'name': 'banana' },
{'id' : 5050, name 'jackfruit'}
]
output should be
let result = [
{ 'id': 1010, 'name': 'grapes' },
{ 'id': 3030, 'name': 'banana' },
{ 'id': 4040, 'name': 'banana' },
{ 'id' : 5050, name 'jackfruit'}
]
here need to get an array which has uncommon object data
ENTRY
{ 'id': 2020, 'name': 'blueberry' } is removed as id and name are commom in both array
These examples use the dataset from the original question but the logic still stands for the updated question.
Depending on the result you want, you can get the difference between the arrays like this:
const result = _.differenceWith(array1, array2, _.isEqual);
That will output
{ id: 1010, name: "grapes" }
If you want the symmetric difference you can concatenate the opposite as well:
const result = _.differenceWith(array1, array2, _.isEqual).concat(_.differenceWith(array2, array1, _.isEqual));
This will give you
{ id: 1010, name: "grapes" }
{ id: 1010, name: "apple" }
{ id: 3030, name: "banana" }
The result you have quoted in your question is slightly different, it is neither difference or symmetric difference, if you only want one result for each ID you would need to remove the second occurence of any object that has an ID key that already exists like so:
result = result.filter((elm, i) => !result.find((elm2, j) => elm.id === elm2.id && i > j) );
that will give you
{ id: 1010, name: "grapes" }
{ id: 3030, name: "banana" }
Incase you want to roll your own.
The following code finds the union, sorts it by the relevant property, and then traverses it. If a key is the same as the previous, then a duplicate has been found and both elements are removed.
function except(a,b) {
const union = [...a, ...b]
union.sort(({id: id1}, {id: id2}) => id1 - id2)
const except = []
let previous = null
for(const el of union) {
if(el.id === previous) {
except.pop()
continue;
}
except.push(el)
previous = el.id
}
return except
}
let arr1 = [
{ 'id': 1010, 'name': 'grapes' },
{ 'id': 2020, 'name': 'blueberry' },
{ 'id': 3030, 'name': 'banana' }
]
let arr2 = [
{ 'id': 0000, 'name': 'apple' },
{ 'id': 2020, 'name': 'blueberry' },
{ 'id': 4040, 'name': 'banana' },
{'id' : 5050, name: 'jackfruit'}
]
console.log(except(arr1, arr2))

how can i convert 1 array or separate its values to multiple array in javascript?

how can i convert 1 array or separate its values to multiple array in javascript?
i got an array like this:
const data =
[
{
'Name': 'John'
'Age': 20
'Company': 'Google'
},
{
'Name': 'Philip'
'Age': 21
'Company': 'Mozzila'
},
{
'Name': 'Matthew'
'Age': 22
'Company': 'Microsoft'
},
{
'Name': 'Peter'
'Age': 23
'Company': 'Accenture'
},
]
how can i transform that array like this:
data = {
'Data1':['John', 'Philip','Matthew', 'Peter'],
'Data2':[20,21,22,23]
'Data3':['Google','Mozzila','Microsoft','Accenture']
}
or like this:
data = {
'Name':['John', 'Philip','Matthew', 'Peter'],
'Age':[20,21,22,23]
'Company':['Google','Mozzila','Microsoft','Accenture']
}
Here's an answer using reduce:
const data = [
{
'Name': 'John',
'Age': 20,
'Company': 'Google'
},
{
'Name': 'Philip',
'Age': 21,
'Company': 'Mozzila'
},
{
'Name': 'Matthew',
'Age': 22,
'Company': 'Microsoft'
},
{
'Name': 'Peter',
'Age': 23,
'Company': 'Accenture'
},
];
const results = data.reduce((a, e) => {
for (const key in e) {
if (!(key in a)) {
a[key] = [];
}
a[key].push(e[key]);
}
return a;
}, {});
console.log(results);
The idea is for each object in data, loop over its keys and push each value into an array in the destination object. This might be a little slow, but it works regardless of the names of your keys and is reasonably terse.
Below uses reduce to iterate over the data array and build an object as it does. This is done dynamically by iterating over each object's keys.
The consistency of your objects are essential. If one object has more keys than another, the array indexes will no longer be kept in sync.
const data = getData();
const obj = data.reduce((obj, curr) => {
Object.keys(curr).forEach(key => {
if (!Array.isArray(obj[key]))
obj[key] = []
obj[key].push(curr[key])
})
return obj
}, {})
console.log(obj);
// Data:
function getData() {
return [{
'Name': 'John',
'Age': 20,
'Company': 'Google'
},
{
'Name': 'Philip',
'Age': 21,
'Company': 'Mozzila'
},
{
'Name': 'Matthew',
'Age': 22,
'Company': 'Microsoft'
},
{
'Name': 'Peter',
'Age': 23,
'Company': 'Accenture'
}
]
}
Alternative
An alternative is to describe which key to keep. Notice I've removed some of your key/value pairs from the data structure. See how the resulting object is kept in sync by inserting an undefined in the value's place:
const data = getData(),
keys = ['Name', 'Age', 'Company'];
const obj = data.reduce((result, item) => {
keys.forEach(key => {
if (!Array.isArray(result[key]))
result[key] = []
result[key].push(item[key])
})
return result
}, {})
console.log(obj);
// Data:
function getData() {
return [{
'Age': 20,
'Company': 'Google'
},
{
'Name': 'Philip',
'Age': 21
},
{
'Name': 'Matthew',
'Company': 'Microsoft'
},
{
'Name': 'Peter',
'Age': 23,
'Company': 'Accenture'
}
]
}
Another approach ,You can have seperate map for each keys . keeping it simple
const data =
[
{
'Name': 'John',
'Age': 20,
'Company': 'Google'
},
{
'Name':'Philip',
'Age': 21,
'Company': 'Mozzila'
},
{
'Name':'Matthew',
'Age': 22,
'Company': 'Microsoft'
},
{
'Name': 'Peter',
'Age': 23,
'Company': 'Accenture'
},
];
let a={'Name':data.map(d=>d.Name),'Age':data.map(d=>d.Age),'Company':data.map(d=>d.Company)};
console.log(a);
var data = [
{
"name" : "xxx",
"age" : 20,
"company" : "aaa"
},
{
"name" : "yyy",
"age" : 30,
"company" : "bbb"
},
{
"name" : "zzz",
"age" : 40,
"company" : "ccc"
}
];
var newData = {};
for(var x =0 ; x < data.length; x++){
for(var obj in data[x]){
if(!newData[obj]){
newData[obj] = [];
}
newData[obj].push( data[x][obj] );
}
}
console.log(newData);
/* Result */
/*
{
name: ["xxx", "yyy", "zzz"],
age: [20, 30, 40],
company: ["aaa", "bbb", "ccc"]
}
*/

Categories