This question already has answers here:
How can I access and process nested objects, arrays, or JSON?
(31 answers)
How to loop through a plain JavaScript object with the objects as members
(28 answers)
Closed 1 year ago.
Given the following array of objects with dates in UTC:
const Arr = [
{
"name": "Person 1",
"date": "2021-02-28T14:00:00.000+0000"
},
{
"name": "Person 2",
"date": "2021-02-28T19:15:00.000+0000"
},
{
"name": "Person 3",
"date": "2021-04-04T18:30:00.000+0000"
},
{
"name": "Person 4",
"date": "2021-05-11T19:00:00.000+0000"
},
{
"name": "Person 5",
"date": "2021-05-12T18:45:00.000+0000"
},
{
"name": "Person 6",
"date": "2021-05-11T19:00:00.000+0000"
},
{
"name": "Person 7",
"date": "2021-05-23T15:00:00.000+0000"
}
];
I grouped the items by date using reduce as described in the below code:
const eventDate = {};
Arr.reduce((groupByDate, event) => {
const date = event.date.split('T')[0];
if (!groupByDate[date]) {
groupByDate[date] = [];
}
groupByDate[date].push(event);
return groupByDate;
}, {});
Getting an object grouped by date (as key) and an array of objects (as values):
{
'2021-02-28': [
{ name: 'Person 1', date: '2021-02-28T14:00:00.000+0000' },
{ name: 'Person 2', date: '2021-02-28T19:15:00.000+0000' }
],
'2021-04-04': [ { name: 'Person 3', date: '2021-04-04T18:30:00.000+0000' } ],
'2021-05-11': [
{ name: 'Person 4', date: '2021-05-11T19:00:00.000+0000' },
{ name: 'Person 6', date: '2021-05-11T19:00:00.000+0000' }
],
'2021-05-12': [ { name: 'Person 5', date: '2021-05-12T18:45:00.000+0000' } ],
'2021-05-23': [ { name: 'Person 7', date: '2021-05-23T15:00:00.000+0000' } ]
}
So my doubt here is how can I loop through that new object in order to get something like this?
(date in UTC will be formatted and get only the time)
2021-02-28:
name: Person 1 time: 14:00
name: Person 2 time: 19:15
2021-04-04:
name: Person 3 time: 18:30
2021-05-11:
name: Person 4 time: 19:00
name: Person 6 time: 19:00
2021-05-12:
name: Person 5 time: 18:45
2021-05-23:
name: Person 7 time: 15:00
Thanks in advance!
I would use Object.entries() or Object.keys() on the top level object to loop over it (for example using forEach() or any other looping method):
Object.entries(data).forEach(([key, items]) => {
console.log(key);
items.forEach(item => {
console.log("name:", item.name, "date:", item.date)
})
})
Playround
Perhaps something like this?
const obj = {
'2021-02-28': [
{ name: 'Person 1', date: '2021-02-28T14:00:00.000+0000' },
{ name: 'Person 2', date: '2021-02-28T19:15:00.000+0000' }
],
'2021-04-04': [{ name: 'Person 3', date: '2021-04-04T18:30:00.000+0000' }],
'2021-05-11': [
{ name: 'Person 4', date: '2021-05-11T19:00:00.000+0000' },
{ name: 'Person 6', date: '2021-05-11T19:00:00.000+0000' }
],
'2021-05-12': [{ name: 'Person 5', date: '2021-05-12T18:45:00.000+0000' }],
'2021-05-23': [{ name: 'Person 7', date: '2021-05-23T15:00:00.000+0000' }]
}
const newObj = {}
const newSubObj = {}
for (const [key, value] of Object.entries(obj)) {
newObj[key] = []
newSubObj[key] = []
for (const item of value) {
const date = new Date(item.date)
const subDate = item.date.substr(11, 5)
const hours = date.getUTCHours()
const minutes = date.getUTCMinutes()
newObj[key].push({ name: item.name, time: `${hours}:${minutes}` })
newSubObj[key].push({ name: item.name, time: subDate })
}
}
console.log(newObj, newSubObj)
Assuming you want to console.log() the result this is how I will do it:
const obj = {
'2021-02-28': [
{ name: 'Person 1', date: '2021-02-28T08:00:00.000+0000' },
{ name: 'Person 2', date: '2021-02-28T19:15:00.000+0000' }
],
'2021-04-04': [ { name: 'Person 3', date: '2021-04-04T18:30:00.000+0000' } ],
'2021-05-11': [
{ name: 'Person 4', date: '2021-05-11T19:00:00.000+0000' },
{ name: 'Person 6', date: '2021-05-11T19:00:00.000+0000' }
],
'2021-05-12': [ { name: 'Person 5', date: '2021-05-12T18:45:00.000+0000' } ],
'2021-05-23': [ { name: 'Person 7', date: '2021-05-23T15:00:00.000+0000' } ]
}
const printObj = (obj) => {
Object.keys(obj).forEach(key => {
console.log(`${key}:`);
obj[key].forEach(val => {
const currentDate = new Date(val.date);
const currentDateHours = (currentDate.getUTCHours() < 10 ? '0' : '') + currentDate.getUTCHours();
const currentDateMinutes = (currentDate.getUTCMinutes() < 10 ? '0' : '') + currentDate.getUTCMinutes();
console.log(`name: ${val.name} time: ${currentDateHours}:${currentDateMinutes}`);
})
});
}
printObj(obj);
Note: getUTCHours() and getUTCMinutes() will not show two digit number in case of a leading 0, I took it into consideration.
Related
I have an array that looks something like this
const example = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
As you can see, the organization name is something I want to key off of and create a data structure like this:
const output = [
// data.value will be their ID
{
organizationName: 'Organization A',
data: [
{ label: 'Person 1', value: '1' },
{ label: 'Person 2', value: '2' },
],
},
{
organizationName: 'Organization B',
data: [
{ label: 'Person 3', value: '3' },
],
},
]
What I've tried
I know I want to use reduce for something like this, but I feel like I'm off:
const providerOptions = externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
} = currentValue;
if (organizationName) {
acc.push({ organization: organizationName, data: [] });
} else {
const { name: externalPersonName, id } = currentValue;
acc[acc.length - 1].data.push({ name: externalPersonName, value: id });
}
return acc;
}, [] as any);
However the output comes out to something like this:
[
{organizationName: 'Organization A', data: []},
{organizationName: 'Organization A', data: []},
{organizationName: 'Organization B', data: []},
];
data doesn't seem to get anything pushed inside the array in this reduce function, and the organization name get duplicated... what am I doing wrong?
Easiest way is to use an Map/Set/or object to keep track of orgs you create. This way you are not searching in the array to see if the organization was found already. After you are done, you can create the array you want from the object.
const externalPeople = {
data : [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
],
};
const providerOptions = Object.values(externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
name: externalPersonName,
id
} = currentValue;
// Is the org new? Yes, create an entry for it
if (!acc[organizationName]) {
acc[organizationName] = { organization: organizationName, data: [] };
}
// push the person to the organization
acc[organizationName].data.push({ name: externalPersonName, value: id });
return acc;
}, {}));
console.log(providerOptions)
Here is another solution
const example = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
const result = example.reduce((res, entry) => {
const recordIndex = res.findIndex(rec => rec.organizationName === entry.organization.name);
if(recordIndex >= 0) {
res[recordIndex].data.push({ label: entry.name, value: entry.id});
} else {
const record = {
organizationName: entry.organization.name,
data: [{ label: entry.name, value: entry.id }]
};
res.push(record);
}
return res;
}, []);
console.log(result);
You are not checking if the value is already present in your accumulation acc
You can check it with a simple find in the if statement since it's an array
const providerOptions = externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
} = currentValue;
//Check if organization is not present already
if (!acc.find(a => a.organization === organizationName)) {
//Add also the data of the element your are processing
acc.push({ organization: organizationName, data: [{label: currentValue.name, value: currentValue.id}] });
} else {
const { name: externalPersonName, id } = currentValue;
acc[acc.length - 1].data.push({ label: externalPersonName, value: id });
}
return acc;
}, [] as any);
I also added the data of the first element of the group you create when adding the organization.
The result should be as your expected output:
[
{
organization: 'Organization A',
data: [
{ label: 'Person 1', value: '1' },
{ label: 'Person 2', value: '2' }
]
},
{
organization: 'Organization B',
data: [
{ label: 'Person 3', value: '3' }
]
}
]
Hope it helps!
Compare this solution (using Lodash) with other solutions. Which one emphasises your intentions at most? This is why we use Lodash in our company - to maintain code as declarative as we can, because code readability, with minimum cognitive overload, is most important goal during coding.
const persons = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
const personsByOrganizations = _.groupBy(persons, 'organization.name')
const output = _.map(personsByOrganizations, (persons, organizationName) => ({
organizationName,
data: _.map(persons, ({ name, id }) => ({
label: name,
value: id
}))
}))
Something like that with using a Set?
result = [...new Set(example.map(d => d.organization.name))].map(label => {
return {
organizationName: label,
data: example.filter(d => d.organization.name === label).map(d => {
return {label: d.name, value: d.id}
})
}
})
`
I have the following arrays of objects, for example:
const data = [
{
date: '01-01',
products: [
{
id: 1,
value: 6,
label: 'Product 1'
},
{
id: 2,
value: 3,
label: 'Product 2'
}
]
},
{
date: '02-01',
products: [
{
id: 1,
value: 4,
label: 'Product 1'
},
]
},
{
date: '03-01',
products: [
{
id: 1,
value: 11,
label: 'Product 1'
},
{
id: 2,
value: 15,
label: 'Product 2'
}
]
}
]
Then I do the grouping and get the following result:
const output = [
{
id: 1,
name: 'Product 1',
data: [6, 4, 11]
},
{
id: 2,
name: 'Product 2',
data: [3, 15]
}
]
The problem with the solution is that I cannot take into account the missing value (the object with the date "02-01" does not have an object with id: 2). I need to check that the object does not exist and substitute zero instead of the missing value. Maybe you know how to do it?
Solution code below:
const result = data.map(e => e.products).flat().reduce((acc, product) => {
const index = acc.findIndex(item => item.id === product.id);
if(index === -1) {
acc.push({
id: product.id,
name: product.label,
data: [product.value]
})
return acc;
}
const findIndex = acc[index].data.findIndex((innerNode) => innerNode.id === product.id);
if (findIndex === -1) {
console.log(product.value)
acc[index].data.push(product.value);
return acc;
}
return acc;
}, []);
Expected result:
const output = [
{
id: 1,
name: 'Product 1',
data: [6, 4, 11]
},
{
id: 2,
name: 'Product 2',
data: [3, 0, 15]
}
]
You can do this in three passes:
first, you find all dates. When you first encounter a product, you will set all its values to 0 for each of those dates.
then, you iterate products and ensure that, for each date, they have a value - which will be zero by default.
finally, you format the output.
const data = [
{
date: '01-01',
products: [
{
id: 1,
value: 6,
label: 'Product 1'
},
{
id: 2,
value: 3,
label: 'Product 2'
}
]
},
{
date: '02-01',
products: [
{
id: 1,
value: 4,
label: 'Product 1'
},
]
},
{
date: '03-01',
products: [
{
id: 1,
value: 11,
label: 'Product 1'
},
{
id: 2,
value: 15,
label: 'Product 2'
}
]
}
]
// goal is to fill this for each product
let dateToValues = data.map(d => [d.date, 0]);
// build map of product-id to values-for-each-date
let products = new Map();
data.forEach(d => d.products.forEach(p => {
let values = products.get(p.id)?.data;
if (values === undefined) {
values = new Map(dateToValues); // a copy
products.set(p.id, {label: p.label, data: values});
}
values.set(d.date, p.value);
}))
// generate output, skipping dates and only showing their values
let output = [];
products.forEach((v, id) => output.push({
id: id, name: v.label, data: [... v.data.values()]}));
console.log(output)
I have an array of appointments objects:
let appointments = [
{ _id: 54321, name: 'app 1', date: "2022-01-20T09:00:00+01:00"},
{ _id: 66756, name: 'app 2', date: "2022-01-20T08:00:00+01:00"},
{ _id: 76889, name: 'app 3', date: "2022-01-21T08:00:00+01:00"},
{ _id: 35790, name: 'app 4', date: "2022-01-22T08:00:00+01:00"},
{ _id: 35790, name: 'app 5', date: "2022-01-25T09:00:00+01:00"}
]
my goal is to create a new array based on the days of the appointments and place them inside, like so:
{ days:
{ 2022-01-20: [
{ _id: 54321, name: 'app 1', date: "2022-01-20T09:00:00+01:00"},
{ _id: 66756, name: 'app 2', date: "2022-01-20T08:00:00+01:00"}
]},
{ 2022-01-21: [
{ _id: 76889, name: 'app 3', date: "2022-01-21T08:00:00+01:00"}
]},
{ 2022-01-22: [
{ _id: 35790, name: 'app 4', date: "2022-01-22T08:00:00+01:00"}
]},
{ 2022-01-23: []},
{ 2022-01-24: []},
{ 2022-01-25: [
{ _id: 35790, name: 'app 5', date: "2022-01-25T09:00:00+01:00"}
]},
}
The first 10 characters of 'date' could become the new values (excluding duplicates) and inside them there should be the proper appointments, as they are in the source - only organized by the days.
Another feature that I'm trying to make is inserting empty days between the active days (example in the second code)
Thanks for your help
const appointments = [
{ _id: 54321, name: 'app 1', date: "2022-01-20T09:00:00+01:00"},
{ _id: 66756, name: 'app 2', date: "2022-01-20T08:00:00+01:00"},
{ _id: 76889, name: 'app 3', date: "2022-01-21T08:00:00+01:00"},
{ _id: 35790, name: 'app 4', date: "2022-01-22T08:00:00+01:00"},
{ _id: 35790, name: 'app 5', date: "2022-01-25T09:00:00+01:00"}
];
const appointmentsByDate = appointments.reduce(
(acc, appointment) => {
// split the date string and store index 0 as date variable.
const [date] = appointment.date.split('T');
return {
...acc,
// overwrite or add date key to the accumulator.
// if the date key already exists, spread the existing value into the new value
[date]: [...(acc[date] || []), appointment],
};
},
{} // starting accumulator (acc) value
);
console.log(appointmentsByDate);
This question already has answers here:
Merge two array of objects based on a key
(23 answers)
Closed 1 year ago.
This is two object. One is 'major'. Other one is 'marjorModel'.
const major = [
{
id: 1,
subject: 'math',
title: 'no good'
},
{
id: 2,
subject: 'science',
title: 'good'
}
]
const majorModel = [
{
id: 1,
amount: 23,
date: '2022-03-01'
},
{
id: 3,
amount: 26,
date: '2022-03-01'
}
]
and I want to merge new one
example)
const newObj = [
{
id: 1,
subject: 'math',
title: 'no good',
amount: 23,
date: '2022-03-01'
},
{
id: 2,
subject: 'science',
title: 'good',
amount: 26,
date: '2022-03-01'
}
]
I don't know how to merge different of other object.
please let me know!!
You can use reduce
major.reduce((acc, v, i) => {
acc.push({
...v,
...majorModel[i]
});
return acc;
}, []);
This question already has answers here:
How to sort an array of objects by date?
(4 answers)
Closed 2 years ago.
I have an array :
const arr = [
{ name: 'abc', date: '30/03/2014' },
{ name: 'cde', date: '30/03/2015' },
{ name: 'fgh', date: '20/04/2014' },
{ name: 'xyz', date: '17/09/2014' },
];
How can I sort this array so that the output would be like this:
const arr = [
{ name: 'cde', date: '30/03/2015' },
{ name: 'xyz', date: '17/09/2014' },
{ name: 'fgh', date: '20/04/2014' },
{ name: 'abc', date: '30/03/2014' },
];
// sort the array with date in latest first.
Using Array#sort with your own sorting comperator. For this split the dates and build with taht in the right sequence a new Date which can be compared.
const arr = [
{ name: 'abc', date: '30/03/2014' },
{ name: 'cde', date: '30/03/2015' },
{ name: 'fgh', date: '20/04/2014' },
{ name: 'xyz', date: '17/09/2014' },
];
arr.sort((a,b) => {
let tempA = a.date.split('/');
let tempB = b.date.split('/');
return ((new Date(tempB[2],tempB[1],tempB[0])) - (new Date(tempA[2],tempA[1],tempA[0])));
});
console.log(arr);