Group and count array by date in javascript - javascript

I'm having a problem grouping this data using javascript. Just starting my career using pure javascript and nodeJS for building a website application.
here's my data result using typeORM.
let data = [
{
control_no: '01-55-10-021-000001',
date_created: '2023-01-05',
validated: '1',
date_validated: '2023-01-06',
submitted: '1',
date_submitted: '2023-01-07'
},
{
control_no: '01-55-10-016-000190',
date_created: '2023-01-05',
validated: '1',
date_validated: '2023-01-06',
submitted: '0',
date_submitted: null
},
{
control_no: '01-55-10-001-000001',
date_created: '2023-01-06',
validated: '0',
date_validated: null,
submitted: '0',
date_submitted: null
},
{
control_no: '01-55-10-001-000002',
date_created: '2023-01-06',
validated: '0',
date_validated: null,
submitted: '0',
date_submitted: null
},
{
control_no: '01-55-10-001-000003',
date_created: '2023-01-06',
validated: '0',
date_validated: null,
submitted: '0',
date_submitted: null
},
{
control_no: '01-55-10-001-000004',
date_created: '2023-01-06',
validated: '1',
date_validated: '2023-01-07',
submitted: '1',
date_submitted: '2023-01-08'
},
{
control_no: '01-55-10-001-000005',
date_created: '2023-01-06',
validated: '1',
date_validated: '2023-01-07',
submitted: '0',
date_submitted: null
},
{
control_no: '01-55-10-001-000006',
date_created: '2023-01-06',
validated: '1',
date_validated: '2023-01-07',
submitted: '1',
date_submitted: '2023-01-08'
},
{
control_no: '01-55-10-001-000007',
date_created: '2023-01-09',
validated: '0',
date_validated: null,
submitted: '0',
date_submitted: null
},
{
control_no: '01-55-10-001-000008',
date_created: '2023-01-09',
validated: '0',
date_validated: null,
submitted: '0',
date_submitted: null
}
];
now i want to have this structure of data where i want to count all data as created then count all validated data as validated and all tagged as submitted as submitted by date
let created = [
{
date: '2023-01-05',
count: 2
},
{
date: '2023-01-06',
count: 6
},
{
date: '2023-01-09',
count: 2
}
];
let validated = [
{
date: '2023-01-06',
count: 2
},
{
date: '2023-01-07',
count: 3
}
]
let submitted = [
{
date: '2023-01-07',
count: 1
},
{
date: '2023-01-08',
count: 2
}
]
then sort by date.
i'm not that good in manipulating arrays using javascript, been in PHP in so many years.

You can do this by using 1 reduce.But I am using 3 different reduce function
For Created:
let created = data.reduce((acc, curr) => {
let key = curr.date_created;
if (!acc[key]) {
acc[key] = {
date: key,
count: 1
};
} else {
acc[key].count++;
}
return acc;
}, {});
// convert the object to array
created = Object.values(created);
// sort data by date
created.sort((a,b) => new Date(a.date) - new Date(b.date));
console.log(created)
for validated
let validated = data.reduce((acc, curr) => {
let key = curr.date_validated;
if (!acc[key]) {
acc[key] = {
date: key,
count: 1
};
} else {
acc[key].count++;
}
return acc;
}, {});
// convert the object to array
validated = Object.values(validated);
// remove the null value
validated = validated.filter(val => val.date != null)
// sort the data by date
validated.sort((a,b) => new Date(a.date) - new Date(b.date));
3.For submitted
let submitted = data.reduce((acc, curr) => {
let key = curr.date_submitted;
if (!acc[key]) {
acc[key] = {
date: key,
count: 1
};
} else {
acc[key].count++;
}
return acc;
}, {});
// convert the object to array
submitted = Object.values(submitted);
// remove the null values
submitted = submitted.filter(val => val.date != null)
//sort the data by date
submitted.sort((a, b) => new Date(a.date) - new Date(b.date));
If you still face any issue. Please let me know

Related

Merge all keys into single object with the same Id in JavaScript

I have an array of objects Like that.
var orderResults = [{
id: '1',
name: 'Maya Mahardhani',
payment_amount : 100,
sku: 'ST001802027',
seq: '1'
},
{
id: '1',
name: 'Maya Mahardhani',
payment_amount : 50,
sku: 'ST000703044',
seq: '2'
},
{
id: '2',
name: 'Tara Debu Batara',
payment_amount : 100,
sku: 'ST005101001',
seq: '1'
},
{
id: '3',
name: 'Nikita Gigir',
payment_amount : 100,
sku: 'ST004403030',
seq: '1'
}]
But I am trying to extract the data in the following way.
[{
id: '1',
name: 'Maya Mahardhani',
total_amount : 150,
sku_1: 'ST001802027',
sku_2: 'ST000703044',
},
{
id: '2',
name: 'Tara Debu Batara',
total_amount : 100,
sku_1: 'ST005101001'
},
{
id: '3',
name: 'Nikita Gigir',
total_amount : 100,
sku_1: 'ST004403030'
}]
I give try with the reduce function of JavaScript. But it overwrites the prev key with the old one. My Code Snippet is like that. I think I am closer to solve. But still seeking the help
orderResults.reduce((res, obj) => {
res[obj.id] = {
total_amount : (obj.id in res ? res[obj.id].total_amount : 0) + obj.payment_amount,
name : obj.name,
}
res[obj.id]['sku' + obj.seq]= obj.sku
return res;
},[])
Thank you
Do not create a new object every time.
const result = Object.values(orderResults.reduce((res, obj) => {
res[obj.id] = res[obj.id] ||
{
id: obj.id,
total_amount : 0,
name : obj.name,
};
res[obj.id].total_amount += obj.total_amount;
res[obj.id]['sku' + obj.seq] = obj.sku;
return res;
},[]))
I think this will do what you are trying to do:
orderResults.reduce((res, obj, i) => {
const existingIdx = res.findIndex(r => r.id === obj.id)
if (existingIdx > -1) {
res[existingIdx] = {
...res[existingIdx],
total_amount : res[existingIdx].total_amount ? res[existingIdx].total_amount + obj.payment_amount : res[existingIdx].payment_amount + obj.payment_amount,
name : obj.name,
['sku_' + obj.seq]: obj.sku
}
} else {
res.push({
id: obj.id,
name: obj.name,
total_amount: obj.payment_amount,
['sku_' + obj.seq]: obj.sku
})
}
return res;
},[])
Notice this line will preserve the original object and overwrite any duplicate keys that are defined afterwards:
...res[existingIdx],
Also when your code runs this line:
res[obj.id] = {
it is setting a specific index in the array, which I don't think you want to do. You want to either push (if the object id hasn't been added yet), or overwrite the existing object at the original insertion point when the object with the same id was created.
[{
id: '1',
name: 'Maya Mahardhani',
payment_amount: 100,
sku: 'ST001802027',
seq: '1'
},
{
id: '1',
name: 'Maya Mahardhani',
payment_amount: 50,
sku: 'ST000703044',
seq: '2'
},
{
id: '2',
name: 'Tara Debu Batara',
payment_amount: 100,
sku: 'ST005101001',
seq: '1'
},
{
id: '3',
name: 'Nikita Gigir',
payment_amount: 100,
sku: 'ST004403030',
seq: '1'
}].reduce((acc, current) => {
const { id, name, payment_amount, sku, seq } = current;
const previousRecord = acc[id];
if (typeof previousRecord === 'object') {
return {
...acc,
[id]: {
...previousRecord,
[`sku_${seq}`]: sku,
total_amount: previousRecord.total_amount + payment_amount
}
}
} else {
return {
...acc,
[id]: {
id,
name,
[`sku_${seq}`]: sku,
total_amount: payment_amount
}
}
}
}, {}) // returns an Object; use Object.values to convert to a list

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));

Remove duplicate records with same status and preserve only latest

I have below object.
const data = [
{
status: 1,
date: '2020-12-01',
},
{
status: 1,
date: '2020-11-01',
},
{
status: 2,
date: '2020-12-01',
},
{
status: 4,
date: '2020-12-01',
},
{
status: 5,
date: '2020-12-01',
}
]
I need to filter out records with status 4 and 5.
Also, need to have only latest record for status 1.
So the result would be like below.
const data = [
{
status: 1,
date: '2020-12-01',
},
{
status: 2,
date: '2020-12-01',
},
]
This is what I have tried.
data.filter(obj => [1, 2, 3].includes(obj.status))
.filter(obj => obj.status === 1)
.sort((a, b) => new Date(b.date) - new Date(a.date))
But here I am losing object with other status.
I could do by storing filtered result first and then sorting and picking up the latest record, and use something like below
const result = filteredResult.push(latestRecordWithStatusOne)
But, is it possible to achieve this using the same chaining?
After filtering for status being 1,2 or 3, you can then use Array.reduce to create an object with the latest date for each status value. Since the other status values don't have multiple entries, it's safe to use this code for all of them. You can then use Object.values to create your desired output array:
const data = [{
status: 1,
date: '2020-12-01',
},
{
status: 1,
date: '2020-11-01',
},
{
status: 2,
date: '2020-12-01',
},
{
status: 4,
date: '2020-12-01',
},
{
status: 5,
date: '2020-12-01',
}
]
const out = Object.values(data
.filter(obj => [1, 2, 3].includes(obj.status))
.reduce((c, obj) => {
c[obj.status] = c[obj.status] || obj;
if (obj.date > c[obj.status].date)
c[obj.status].date = obj.date
return c;
}, {})
);
console.log(out);
you can try:
const data = [{ status: 1, date: '2020-12-01', },
{ status: 1, date: '2020-11-01', },
{ status: 2, date: '2020-12-01', },
{ status: 4, date: '2020-12-01', },
{ status: 5, date: '2020-12-01', }
]
console.log([...new Set(Object.values(data
.filter(obj => [1, 2, 3].includes(obj.status))).map(item => item.status))])
You can do this in one go with a reduce function. Ignore the 4/5 status and check for newest status 1 and replace if needed. Otherwise, add the value.
const data = [{
status: 1,
date: '2020-12-01',
},
{
status: 1,
date: '2020-11-01',
},
{
status: 2,
date: '2020-12-01',
},
{
status: 4,
date: '2020-12-01',
},
{
status: 5,
date: '2020-12-01',
}
]
const filtered = data.reduce((accumulator, currentValue, index, array) => {
if ([4, 5].includes(currentValue.status)) {
// don't add value since it's a status 4/5
return accumulator;
} else if (currentValue.status === 1) {
// currentValue status is 1 so check if there is already one in the result
const index = accumulator.findIndex(obj => obj.status === 1)
if (index === -1) {
// no other status 1 so add it
return [...accumulator, currentValue]
} else if (accumulator[index].date < currentValue.date) {
// newer status 1 so replace it
return [...accumulator.slice(0, index), currentValue, ...accumulator.slice(index + 1)]
} else {
// already the newest so just return the current accumulator
return accumulator;
}
} else {
// any other status can just be added
return [...accumulator, currentValue];
}
}, []);
console.log(filtered)

How do I filter a list using parmeters within a nested object?

I have a list that I want to filter using parameters from advancedSearchFilters which has nested objects. I want to return a list that matches all parameters if any.
const list = [
{
additionalPrices: 0,
clientOrderNumber: 'N / A',
companyName: 'Test Company',
completeDate: 'Invalid date',
createdAt: '2019-09-08 16:41:43',
createdBy: 'test#test.com',
deliveryDate: '2019-09-14 16:39:25',
driver: 'Robert Kaszkiewicz',
dropOff: 'Avenida Callao 1234, Buenos Aires, Argentina',
height: 23,
isPreparingTrailer: true,
isSmsSent: true,
isTrailerReady: true,
key: '2104a7bd-5e78-49ce-a1f6-0699ac98f264',
notes: '',
pickUp: 'Poprad, Slovakia',
price: 1400,
quantity: 2,
trailer: 'einzeln',
vehicle: 'Sattel',
vehicleClass: 'Mega',
vehicleId: '12313',
vehicleReadyDate: '2019-09-27 16:41:22',
weight: 123
},
{
clientOrderNumber: 'N / A',
companyName: 'Test Company',
createdAt: '2019-09-26 18:32:18',
createdBy: 'test#test.com',
deliveryDate: '2019-09-20 18:31:45',
driver: 'Michal Kucharski',
dropOff: 'Logroño, Spain',
height: 0,
isPreparingTrailer: false,
isSmsSent: false,
isTrailerReady: false,
key: '388113f5-3927-4fe3-80d5-f2fcf1c7cedd',
notes: '',
orderId: '',
pickUp: '16671 Butano Place, Fontana, CA, USA',
price: 0,
quantity: 1,
trailer: '2er Pack',
vehicle: 'Sattel',
vehicleClass: 'Standard',
vehicleId: 'efgefg',
vehicleReadyDate: '2019-09-27 18:32:08',
weight: 0
},
{
clientOrderNumber: 'N / A',
companyName: 'Test Company',
createdAt: '2019-10-07 14:59:28',
createdBy: 'test#test.com',
deliveryDate: '2019-10-09 14:54:44',
driver: 'Pawel Marczyk',
dropOff: 'München, Germany',
height: 1150,
isPreparingTrailer: false,
isSmsSent: false,
isTrailerReady: false,
key: '795e7531-8655-4e38-a576-e13483922607',
notes: 'alle fertig, vorher anrufen',
orderId: '',
pickUp: 'Soest, Germany',
price: 5000,
quantity: 10,
trailer: '3er Pack',
vehicle: 'Sattel',
vehicleClass: 'Standard',
vehicleId: '1121+23+24',
vehicleReadyDate: '2019-10-07 14:55:19',
weight: 20000
},
];
const advancedSearchFilters = {
companyName: {
modifier: 'equals',
value: 'Test Company'
},
price: {
modifier: 'greaterThan',
value: '54'
},
driver: {
modifier: 'equals',
value: 'Robert'
}
};
const handleFilterModifier = (value, compareValue, modifier) => {
if (modifier === 'lessThan') {
return value > compareValue;
}
if (modifier === 'equals') {
return value === compareValue;
}
if (modifier === 'greaterThan') {
return value < compareValue;
}
return null;
};
const resultList = list.filter(
item => Object.entries(advancedSearchFilters).every(([filterKey, filterVal]) => {
const compareValue = item[filterKey].value;
const value = keys[filterKey];
list = handleFilterModifier(
parseFloat(value),
compareValue,
filterVal
);
return list;
})
);
The resultList function above returns an error.
I guess the problem arises from trying to get the query parameters from within advancedSearchFilters. This code will help me accomplish an advanced search feature request.
As user3773048 says in the comment, Object doesn't have a filter method. Array, however, does.
Instead of list.filter(item => ...), you can pull your inner function out, maybe call it predicate like so:
const predicate = item => ...
Then use it like so:
const resultList = Object.entries(list).reduce(
(filteredList, [key, item]) => {
if (predicate(item)) {
filteredList[key] = item;
}
return filteredList;
},
{}
);
The Object.entries() method gives you the key/value pairs of the object as an array, and reduce() lets you condense the list back into key/value pairs in an object.

mongoose object doesh't work like local object

I'm using below function for building a tree list and is okey with local obj_list object, Why That not work with mongodb doc object?
Code:
const promise = Tree.find({uid: mongoose.Types.ObjectId('5c17974259bf01254c9c7f56')}, {'_id': false, 'uid': false}).exec();
promise.then(async (doc) => {
const obj_list = doc; // not work with doc object in db
const obj_list = [{ // work localy
nid: 1,
name: 'father',
parent: '0',
__v: 0,
},
{
nid: 2,
name: 'boy',
parent: '1',
__v: 0,
}];
console.log(doc);
console.log(obj_list);
const obj_nested_list = [];
let obj;
function fill_with_children(children_arr, parent_id) {
for (let i = 0; i < obj_list.length; i++) {
obj = obj_list[i];
if (obj.parent == parent_id) {
children_arr.push(obj);
obj.children = [];
fill_with_children(obj.children, obj.nid);
}
}
}
fill_with_children(obj_nested_list, 0);
console.log(obj_nested_list);
}).catch((err) => {
if (err) {
console.log(err);
}
});
console.log(doc):
[ { nid: 1, name: 'father', parent: '0', __v: 0 },
{ nid: 2, name: 'boy', parent: '1', __v: 0 } ]
console.log(obj_list):
[ { nid: 1, name: 'father', parent: '0', __v: 0 },
{ nid: 2, name: 'boy', parent: '1', __v: 0 } ]
output with doc: // not ok
[ { nid: 1, name: 'father', parent: '0', __v: 0 } ]
output with obj_list: // ok
[ { nid: 1,
name: 'father',
parent: '0',
__v: 0,
children: [ [Object] ] } ]
Mongoose query promises resolve to a Document object, not a plain JavaScript object. If you want a plain JavaScript object to manipulate and log, you should use Query#lean:
const promise = Tree.find(…).lean().exec();
promise.then(…);

Categories

Resources