JS - merge object same array lodash - javascript

I need to transform a array of multiple object in one array object,
I explain myself, I wish to group for each table the objects that carry the same "month" and replace the properties that have the same value by adding their ID at the beginning:
ex:
quantity: 1
becomes fpsIssuedQuantity (in camelCase). http://jsfiddle.net/rLjQx/96589/
here are my data :
var data = {
"2018-01": [
{ id:"fpsIssued", month:"2018-01", quantity:"28" },
{ id:"dgfipIncome", month:"2018-01", amount:1350 },
{ id:"antaiPaidFps", month:"2018-01", quantity:2242 }
],
"2018-02": [
{ id: "fpsIssued", month: "2018-02", quantity: "29" },
{ id: "dgfipIncome", month: "2018-02", amount: 8530 },
{ id: "antaiPaidFps", month: "2018-02", quantity: 4857}
]
};
console.log(data);
and the expected data :
var expectedData = {
"2018-01": [
{ month: "2018-01", fpsIssuedquantity: "28",
dgfipIncomeamount: 1350, antaiPaidFpsQuantity: 2242
}
],
"2018-02": [
{ month: "2018-02", fpsIssuedquantity: "29",
dgfipIncomeamount: 8530, antaiPaidFpsQuantity: 4857
}
]
};
console.log(expectedData);
i use lodash and angularjs but i can not get my result .. please could you help me?

You could map new objects with wanted new property names and values in new objects.
var data = { "2018-01": [{ id: "fpsIssued", month: "2018-01", quantity: "28" }, { id: "dgfipIncome", month: "2018-01", amount: 1350 }, { id: "antaiPaidFps", month: "2018-01", quantity: 2242 }], "2018-02": [{ id: "fpsIssued", month: "2018-02", quantity: "29" }, { id: "dgfipIncome", month: "2018-02", amount: 8530 }, { id: "antaiPaidFps", month: "2018-02", quantity: 4857 }] },
result = Object.assign(
...Object
.entries(data)
.map(
([k, v]) => ({ [k]: Object.assign(...v.map(o => ({ month: o.month, [o.id + ('quantity' in o ? 'Quantity' : 'Amount')]: 'quantity' in o ? o.quantity : o.amount }))) })
)
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Group Array of Objects to Year and Month by Date

Given the following Array of Objects:
[{
"id": 1,
"name": "random_name1",
"published_at": "2021-01-16T08:52:24.408Z",
},
{
"id": 2,
"name": "random_name2",
"published_at": "2022-02-16T08:52:24.408Z",
},
{
"id": 3,
"name": "random_name3",
"published_at": "2020-04-16T08:52:24.408Z",
},
{
"id": 4,
"name": "random_name4",
"published_at": "2020-04-16T08:52:24.408Z",
},
{
"id": 5,
"name": "random_name5",
"published_at": "2022-05-16T08:52:24.408Z",
}
]
I need to group the items in one array of nested objects (descending) by Year and Month, result should be:
[
{
year: '2022',
months: [
{
month: '5',
items: [
{
id: '5',
name: 'random_name5'
}
]
},
{
month: '2',
items: [
{
id: '2',
name: 'random_name2'
}
]
}
]
},
{
year: '2021',
months: [
{
month: '1',
items: [
{
id: '1',
name: 'random_name1'
}
]
},
{
month: '2',
items: [
{
id: '2',
name: 'random_name2'
}
]
}
]
},
{
year: '2020',
months: [
{
month: '4',
items: [
{
id: '3',
name: 'random_name3'
},
{
id: '4',
name: 'random_name4'
}
]
}
]
}
];
I have tried the following:
items = [...new Set(items.map((item) => parseInt(item.published_at.split('-')[0])))].map((year) => [
{
year: year,
months: [
...new Set(
items
.filter((item) => parseInt(item.published_at.split('-')[0]) === year)
.map((item) => parseInt(item.published_at.split('-')[1]))
)
].map((month) => [
{
month: month,
items: items.filter(
(item) => parseInt(item.published_at.split('-')[0]) === year && parseInt(item.published_at.split('-')[1]) === month
)
}
])
}
]);
return items
The problem with the above solution, is that it will create a two dimensional array like so (months being two dimensional too):
[
[ { year: 2022, months: [Array] } ],
[ { year: 2021, months: [Array] } ],
[ { year: 2020, months: [Array] } ],
[ { year: 2019, months: [Array] } ],
[ { year: 2018, months: [Array] } ]
]
How to fix this?
If you get a unique list of year-months you can use this to map your object
const items = [{ "id": 1,"name": "random_name1","published_at": "2021-01-16T08:52:24.408Z", },
{ "id": 2, "name": "random_name2", "published_at": "2022-02-16T08:52:24.408Z",},
{ "id": 3, "name": "random_name3","published_at": "2020-04-16T08:52:24.408Z",},
{"id": 4, "name": "random_name4", "published_at": "2020-04-16T08:52:24.408Z",},
{ "id": 5, "name": "random_name5", "published_at": "2022-05-16T08:52:24.408Z",}]
let uniqueYearMonths = [... new Set(items.map(x => x.published_at.substring(0,7)))];
let results = [... new Set(items.map(x => x.published_at.substring(0,4)))]
.map(year => ({
year: year,
months: uniqueYearMonths
.filter(ym => ym.startsWith(year))
.map(ym => ({
month: ym.substring(5,7),
items: items
.filter(item => item.published_at.startsWith(ym))
.map(item => ({
id: item.id,
name: item.name
}))
}))
}));
console.log(results);
Given you array as data, you could do something with array methods like map and reduce.
Like this:
const groupedByYear = data.map((e) => ({ ...e, published_at: new Date(e.published_at) }))
.reduce((acc, e) => {
const year = e.published_at.getFullYear();
const month = e.published_at.getMonth() + 1;
if (!acc[year]) acc[year] = { year };
if (!acc[year][month]) acc[year][month] = [];
acc[year][month] = e;
return acc;
}, {})
const result = Object.values(groupedByYear).reduce((acc, e) => {
const { year, ...months } = e;
acc.push({ year: year, months: months });
return acc;
}, [])
This is an example and is probably not the best way to do this. It is only intended to show you a path of data transformations.
First data.map to be able to do operations on dates. Then a reduce to group data (here using an object). Then creating an array from the object values to match the output you want.
Compared to a solution like you showed, there is the advantage that you limit the number of times that you iterate over the array. It is always a good idea to avoid iterating to much time on an array for better performance.

Group array by year and month

I would like to group an array with events by year and month. My data looks like this:
const events = [
{
name: "event 1",
year: 2021,
month: 1,
},
{
name: "event 2",
year: 2021,
month: 9,
},
{
name: "event 3",
year: 2021,
month: 1,
},
{
name: "event 4",
year: 2022,
month: 7,
},
]
And my expected outcome should be something like this:
[
{
year: 2021,
month: 1,
events: [
{
name: "event 1"
},
{
name: "event 3"
}
]
},
{
year: 2021,
month: 9,
events: [
{
name: "event 2"
}
]
}
]
What would be the best approach to do this? I found a couple stackoverflow posts to group an array by it's key value but that not what I'm looking for.
const groupBy = (array, key) => {
return array.reduce((result, currentValue) => {
// If an array already present for key, push it to the array. Else create an array and push the object
(result[currentValue[key]] = result[currentValue[key]] || []).push(currentValue);
// Return the current iteration `result` value, this will be taken as next iteration `result` value and accumulate
return result;
}, {}); // empty object is the initial value for result object
};
const groupedByYear = groupBy(events, 'year');
You can do this with reduce and Object.values
const events = [
{
name: "event 1",
year: 2021,
month: 1,
},
{
name: "event 2",
year: 2021,
month: 9,
},
{
name: "event 3",
year: 2021,
month: 1,
},
];
const result = Object.values(events.reduce( (acc,evt) => {
const key = `${evt.year}-${evt.month}`;
if(!acc[key]) {
acc[key] = {year: evt.year, month: evt.month, events:[]}
}
acc[key].events.push( {name:evt.name} );
return acc;
},{}));
console.log(result);
You could take a dynamic approach by using a combined key for wanted properties for grouping.
Then remove all keys of grouing and push a new object without unwanted properties.
const
events = [{ name: "event 1", year: 2021, month: 1 }, { name: "event 2", year: 2021, month: 9 }, { name: "event 3", year: 2021, month: 1 }],
keys = ['year', 'month'],
result = Object.values(events.reduce((r, o) => {
let value,
key = keys.map(k => o[k]).join('|');
if (!r[key]) r[key] = { ...Object.fromEntries(keys.map(k => [k, o[k]])), events: [] };
r[key].events.push(keys.reduce((t, k) => (({ [k]: value, ...t } = t), t), o));
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to Group JavaScript Array of Object based on key

So I have a data like this
const carts = [
{
name: 'Voucher A',
participants: [
{
date: 112
},
{
date: 112
}
],
supplierName: 'ABC',
ticketDescription: 'Description of',
...data
},
{
name: 'Voucher B',
participants: [
{
date: 111
},
{
date: 112
}
],
supplierName: 'ABC',
ticketDescription: 'Description of',
...data
}
]
And I want to group it based on the date (if it has same date). So for data above, the expected result will be
expected = [
{
name: 'Voucher A',
date: 1,
count: 1,
supplierName: 'ABC',
ticketDescription: 'Description of',
...data
},
{
name: 'Voucher A',
date: 2,
count: 1,
supplierName: 'ABC',
ticketDescription: 'Description of',
...data
}
]
Because it has different date. But if it has same date, the expected result will be
expected = [
{
name: 'Voucher A',
date: 1,
count: 2,
supplierName: 'ABC',
ticketDescription: 'Description of',
...data
}
]
I was trying to use reduce to group it but it did not give the structure I want
carts.forEach(cart => {
cart.participants.reduce((acc, obj) => {
acc[obj.date] = [...acc[obj.date] || [], obj]
return acc
}, {})
})
To organize the data, I think you need two associations to group by: the name and the dates and their counts for that name:
const carts = [
{
name: 'Voucher A',
participants: [
{
date: 1
},
{
date: 2
}
]
}
];
const groupedByNames = {};
for (const { name, participants } of carts) {
if (!groupedByNames[name]) groupedByNames[name] = {};
for (const { date } of participants) {
groupedByNames[name][date] = (groupedByNames[name][date] || 0) + 1;
}
}
const output = Object.entries(groupedByNames).flatMap(
([name, dateCounts]) => Object.entries(dateCounts).map(
([date, count]) => ({ name, date: Number(date), count })
)
);
console.log(output);
If you want use, just plain for loops, you can try this solution. It looks simple and elegant 😜😜
const carts = [
{
name: 'Voucher A',
participants: [
{
date: 1
},
{
date: 1
},
{
date: 2
}
]
},
{
name: 'Voucher B',
participants: [
{
date: 1
},
{
date: 2
},
{
date: 2
}
]
}
]
const finalOutput = []
for (const cart of carts) {
for (const participant of cart.participants) {
const res = finalOutput.find(e => e.name === cart.name && e.date === participant.date)
if (res) {
res.count += 1
} else {
finalOutput.push({ name: cart.name, date: participant.date, count: 1 })
}
}
}
console.log(finalOutput)
Use forEach and destructuring
const process = ({ participants, name }) => {
const res = {};
participants.forEach(({ date }) => {
res[date] ??= { name, count: 0, date };
res[date].count += 1;
});
return Object.values(res);
};
const carts = [
{
name: "Voucher A",
participants: [
{
date: 1,
},
{
date: 2,
},
],
},
];
console.log(carts.flatMap(process));
const carts2 = [
{
name: "Voucher A",
participants: [
{
date: 1,
},
{
date: 1,
},
],
},
];
console.log(carts2.flatMap(process));

How to merge merge multiple array to single array in javascript

I have multiple array in which there are multiple types of data such as:
var student= [{
id : 1,
name : 'name1',
year: 2016,
dist_id: 251,
zone_id: 25106
},
{
id : 1,
name : 'name2',
year: 2018,
dist_id: 252,
zone_id: 25212
},];
var dist= [{
id : 251,
name : 'dist1'
},
{
id : 252,
name : 'dist2'
}];
var zone= [{
id : 25106,
name : 'zone1'
},
{
id : 25212,
name : 'zone2'
}];
I want to create an array that combines all the data into one, so that the fields in the array look like this:
var merge = [{
id: 1,
name : 'name1',
year: 2016,
distname : 'dist1',
zonename: 'zone1',
},
{
id: 2,
name : 'name2',
year: 2018,
distname : 'dist2',
zonename: 'zone2',
}];
Thanks in advance.
You could take a Map for distance and zone and map the values in new objects.
var student = [{ id: 1, name: 'name1', year: 2016, dist_id: 251, zone_id: 25106 }, { id: 1, name: 'name2', year: 2018, dist_id: 252, zone_id: 25212 }],
dist = [{ id: 251, name: 'dist1' }, { id: 252, name: 'dist2' }],
zone = [{ id: 25106, name: 'zone1' }, { id: 25212, name: 'zone2' }],
distMap = new Map(dist.map(({ id, name: distname }) => [id, { distname }])),
zoneMap = new Map(zone.map(({ id, name: zonename }) => [id, { zonename }])),
merged = student.map(({ id, name, year, dist_id, zone_id }) => Object.assign(
{ id, name, year },
distMap.get(dist_id),
zoneMap.get(zone_id)
));
console.log(merged);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could map() the student array to the required merge array where, for each mapping iteration, you would search the dist and zone arrays for items that match on dist_id and zone_id respectively, and merge the name of the into the mapped result:
var student= [
{ id : 1, name : 'name1', year: 2016, dist_id: 251, zone_id: 25106 },
{ id : 1, name : 'name2', year: 2018, dist_id: 252, zone_id: 25212 } ];
var dist= [
{ id : 251, name : 'dist1' }, { id : 252, name : 'dist2' }];
var zone= [
{ id : 25106, name : 'zone1' }, { id : 25212, name : 'zone2' } ];
// Perform a mapping over the student array to aquire merge array in required
// format, with required distname/zonename data
var merge = student.map((s) => {
// Search dist and zone arrays for items that match of dist_id/zone_id
// by filtering and mapping these arrays to find distname and zonename
// for this student
const distname = dist.filter(d => d.id === s.dist_id).map(d => d.name)[0];
const zonename = zone.filter(z => z.id === s.zone_id).map(z => z.name)[0];
return {
id : s.id,
name : s.name,
year : s.year,
distname : distname,
zonename : zonename
}
});
console.log('required merge array:', merge)

Filter through array of objects in javascript

I have a data structure that looks like this:
const carsData = [
{
name: "Cars",
collection: [
{ year: 2011, model: "B", price: 4400 },
{ year: 2015, model: "A", price: 32000 },
{ year: 2016, model: "B", price: 15500 }
]
},
{
name: "Trucks",
collection: [
{ year: 2014, model: "D", price: 18000 },
{ year: 2013, model: "E", price: 5200 }
]
},
{
name: "Convertibles",
collection: [
{ year: 2009, model: "F", price: 20000 },
{ year: 2010, model: "G", price: 8000 },
{ year: 2012, model: "H", price: 12500 },
{ year: 2017, model: "M", price: 80000 }
]
}
];
and want to return a new array let's say const newCarsData(see below) where collection consists of only objects with year higher than 2013, so it will look like this:
const newCarsData = [
{
name: "Cars",
collection:[
{ year: 2015, model: "A", price: 32000 },
{ year: 2016, model: "B", price: 15500 }
]
},
{
name: "Trucks",
collection: [
{ year: 2014, model: "D", price: 18000 }
]
},
{
name: "Convertibles",
collection: [
{ year: 2017, model: "M", price: 80000 }
]
}
];
I tried filter method collection.filter(x => x.year > 2013) inside of for loop, but couldn't make it work. At the end my code looked like this
const newCarsData = getNewData(carsData);
let arr = [];
function getNewData(somedata) {
for (let i = 0; i < somedata.length; i++) {
// console.log(somedata[i].collection);
for (let j = 0; j < somedata[i].collection.length; j++) {
let arr.push(somedata[i].collection[j]);
// console.log(somedata[i].collection[j]);
}
// return somedata[i].collection.filter(x => x.year > 2013);
}
return arr.filter(x => x.year > 2013);
}
Since the collection is in another array inside the array items, you can't directly use filter. You can use map first then use filter.
const carsData=[{name:"Cars",collection:[{year:2011,model:"B",price:4400},{year:2015,model:"A",price:32000},{year:2016,model:"B",price:15500}]},{name:"Trucks",collection:[{year:2014,model:"D",price:18000},{year:2013,model:"E",price:5200}]},{name:"Convertibles",collection:[{year:2009,model:"F",price:20000},{year:2010,model:"G",price:8000},{year:2012,model:"H",price:12500},{year:2017,model:"M",price:80000}]}]
const filteredCarData = carsData.map(carType => {
return {
...carType,
collection: carType.collection.filter(car => car.year>2013)
}
})
console.log(JSON.stringify(filteredCarData))
The ...carType notation collects the object properties in the new mapped object. If you have no other properties than name, you can instead do
const filteredCarData = carsData.map(carType => {
return {
name: carType.name,
collection: carType.collection.filter(car => car.year>2013)
}
})
You'll have to update your collection:
carsData.forEach(function(carData){
carData.collection = carData.collection.filter(x => x.year > 2013);
});
One way to do it can be using reduce.
var res = carsData.reduce((acc, value) => {
let data = { name: value.name, collections: value.collection.filter(v => v.year > 2013 )}
return acc.concat(data)
}, [])
You can actually, replace the reduce with a map:
carsData.map(value => {
return { name: value.name, collections: value.collection.filter(v => v.year > 2013 )}
})

Categories

Resources