I have the following objects
Person {
name: string
birthday: Date
lifeEvents: LifeEvent[]
}
LifeEvent {
eventId: number
message: string
comments: string
}
As the data comes in chunks, I will have an array of Person with one object that has name and birthday with values but lifeEvents is empty (Think of this one like a parent object.)
All other objects won't have birthday and name populated and will have only one LifeEvent with either eventId and message with values or eventId and comments
Within that array, I need to get the parent object, which has name and birthday with values, then get all lifeEvents from the remaining objects, merge all items that contains same eventId into a LifeEvent then push it to lifeEvents of the parent.
I have tried array.reduce, array.map but can't figure out a way of combining those objects into one.
My output should be only one Person with all lifeEvents merged by eventId
Sample data:
let results = [
{
name: 'Test1',
birthday: '2022-06-14',
lifeEvents: null
},
{
name: null,
birthday: null,
lifeEvents: [
{
eventId: 1,
message: 'First event',
comments: null
}
]
},
{
name: null,
birthday: null,
lifeEvents: [
{
eventId: 2,
message: 'Second event',
comments: null
}
]
},
{
name: null
birthday: null
lifeEvents: [
{
eventId: 1,
message: null,
comments: 'First event comment'
}
]
},
{
name: null
birthday: null
lifeEvents: [
{
eventId: 2,
message: null,
comments: 'Second event comment'
}
]
},
]
Appreciate any help.
Premise: The data structure you are using is wrong, you should try to use arrays with homogenous models.
That said I used a reduce method, with a condition to treat the first element in a different way from the other ones.
Last thing, you said merge lifeEvents, I assume you meant to overwrite the nullish values for events with same ids, if you want to overwrite all values then you can omit the merge utility function I wrote.
let results = [{
name: 'Test1',
birthday: '2022-06-14',
lifeEvents: null,
},
{
name: null,
birthday: null,
lifeEvents: [{
eventId: 1,
message: 'First event',
comments: null,
}, ],
},
{
name: null,
birthday: null,
lifeEvents: [{
eventId: 2,
message: 'Second event',
comments: null,
}, ],
},
{
name: null,
birthday: null,
lifeEvents: [{
eventId: 1,
message: null,
comments: 'First event comment',
}, ],
},
{
name: null,
birthday: null,
lifeEvents: [{
eventId: 2,
message: null,
comments: 'Second event comment',
}, ],
},
];
const merge = (o1, o2) => {
const r = {...o1}
Object.keys(o1).forEach(k => r[k] = o2[k] || o1[k])
return r
}
const r = results.reduce((o, curr, i) => {
if (i === 0) {
return { ...o,
lifeEvents: []
};
} else {
const currentEvent = curr.lifeEvents[0]
const idx = o.lifeEvents.findIndex((_o) => _o.eventId === currentEvent.eventId);
if (idx !== -1) return { ...o,
lifeEvents: o.lifeEvents.map((_o, i) => i === idx ? merge(_o, currentEvent) : _o)
}
else return { ...o,
lifeEvents: [...o.lifeEvents, currentEvent]
}
}
}, results[0]);
console.log("RESULT:", r);
The following produces the requested result based on examples provided:
let results = [
{
name: 'Test1',
birthday: '2022-06-14',
lifeEvents: null
},
{
name: null,
birthday: null,
lifeEvents: [
{
eventId: 1,
message: 'First event',
comments: null
}
]
},
{
name: null,
birthday: null,
lifeEvents: [
{
eventId: 2,
message: 'Second event',
comments: null
}
]
},
{
name: null,
birthday: null,
lifeEvents: [
{
eventId: 1,
message: null,
comments: 'First event comment'
}
]
},
{
name: null,
birthday: null,
lifeEvents: [
{
eventId: 2,
message: null,
comments: 'Second event comment'
}
]
},
];
// extract parent
const parentResult = results.find((result) => result.name);
// generate unique events from sibling entities
const uniqueEvents = new Map();
results.forEach((result) => result.lifeEvents?.forEach(
(lifeEvent) => {
if (uniqueEvents.has(lifeEvent.eventId)) {
updateEvent(lifeEvent);
} else {
uniqueEvents.set(lifeEvent.eventId, { eventId: lifeEvent.eventId, message: lifeEvent.message, comments: lifeEvent.comments});
}
})
);
// function to update event that is already stored in uniqueEvents
function updateEvent(lifeEvent) {
const existingLifeEvent = uniqueEvents.get(lifeEvent.eventId);
if (lifeEvent.message) existingLifeEvent.message = lifeEvent.message;
if (lifeEvent.comments) {
if (existingLifeEvent.comments) {
existingLifeEvent.comments.concat(lifeEvent.comments)
} else {
existingLifeEvent.comments = lifeEvent.comments;
}
}
}
// populate lifeEvents inside the parentResult
parentResult.lifeEvents = [];
uniqueEvents.forEach((uniqueId) => {
parentResult.lifeEvents.push(uniqueId);
});
console.log(parentResult);
Related
I have an array of objects in the hierarchical data structure. I need to check if any of the parents or children nodes contains a search string. I am able to filter the array, but I just want to update the matchFound property instead of applying the filter on the tree data.
Expected Result:
If match is found, matchFound property of all the parent and children nodes should be true otherwise false.
treeData = [{
name: 'Infiniti',
matchFound: null,
children: [{
name: 'G50',
matchFound: null,
children: [{
name: 'Pure AWD',
matchFound: null,
},
{
name: 'Luxe',
matchFound: null,
},
],
},
{
name: 'QX50',
matchFound: null,
children: [{
name: 'Pure AWD',
matchFound: null,
},
{
name: 'Luxe',
matchFound: null,
},
],
},
],
},
{
name: 'BMW',
matchFound: null,
children: [{
name: '2 Series',
matchFound: null,
children: [{
name: 'Coupé',
matchFound: null,
},
{
name: 'Gran Coupé',
matchFound: null,
},
],
},
{
name: '3 Series',
matchFound: null,
children: [{
name: 'Sedan',
matchFound: null,
},
{
name: 'PHEV',
matchFound: null,
},
],
},
],
},
];
filteredData = [];
function filter(searchString) {
this.filteredData = this.search(this.treeData, searchString);
console.log(this.filteredData)
}
function search(children, searchString) {
return children.reduce((acc, item) => {
if (item.name.toLowerCase().includes(searchString.toLowerCase())) {
acc.push(item);
} else if (item.children && item.children.length > 0) {
const newItems = this.search(item.children, searchString);
if (newItems.length > 0) {
acc.push({
name: item.name,
children: newItems
});
}
}
return acc;
}, []);
}
this.filter('infiniti');
this.filter('luxe');
I can propose a solution wiht some assumptions:
The filter method should return new tree structure, and should not modify the original one
If any of the child nodes has match - all the parents are marked as matchFound=true
Here is a working playground
And I'll paste the meaningful parts of the code:
function filter(searchString: string) {
return treeData.map(node => search(node, searchString));
}
function search(node: CarNode, searchString: string, foundInParent = false): CarNode {
const matchFoundInNodeOrParent = foundInParent || node.name.toLowerCase().includes(searchString.toLowerCase());
const children = node?.children?.map(child => search(child, searchString ,matchFoundInNodeOrParent));
//match is found if parent or this node or at least one of th children has mathc found
const matchFound = matchFoundInNodeOrParent || ( children !== undefined && children.some(child => child.matchFound));
//return new object
return children !== undefined ? { ...node, matchFound, children } : { ...node, matchFound };
}
Here I have attendance details and I Want to filter every data that contains employees id:1.
for example: I have data like this:
const attendance = [
{
date: 1,
employees: [
{
id: 1,
name: 'mahadev',
status: 'p'
},
{
id: 2,
name: 'roshan',
status: 'p'
},
]
},
{
date: 2,
employees: [
{
id: 1,
name: 'mahadev',
status: 'a'
},
{
id: 2,
name: 'roshan',
status: 'p'
},
]
},
];
And I want Output like this:
[
{
date:1,
employees: [
{
id:1,
name:'mahadev',
status:'p'
}
]
},
{
date:2,
employees: [
{
id:1,
name:'mahadev',
status:'a'
}
]
},
]
Try using map and filter.
const attendance = [{
date: 1,
employees: [
{ id: 1, name: 'mahadev', status: 'p' },
{ id: 2, name: 'roshan', status: 'p' }
]
},
{
date: 2,
employees: [
{ id: 1, name: 'mahadev', status: 'a' },
{ id: 2, name: 'roshan', status: 'p' }
]
},
];
const filtered = id =>
attendance.map(a => {
const employees = a.employees.filter(e => e.id === id);
return { ...a, employees };
});
console.log(filtered(1));
Using map() and filter()
const filtered = id =>
attendance.map(a => {
const employees = a.employees.filter(emp => emp.id === id);
return { date:a.date, employees };
});
console.log(filtered(1))
I have the following object in javascript:
let tvshow = {
id: 268592,
name: 'The 100',
status: 'Continuing',
episodes: {
'1': {
'1': {
airdate: '2014-03-20',
file_size: 0,
location: '',
name: 'Pilot',
quality: 'N/A',
release_name: '',
status: 'Skipped',
subtitles: ''
},
'2': {
airdate: '2014-03-27',
etc: 'etc.'
}
'3': {
airdate: '2052-03-27',
etc: 'etc.'
}
},
};
I want to filter out all episodes with airing date in the future (airdate >= currentDate). Current date is calculated as:
let currentDate = new Date().toISOString().substring(0, 10);
After selection I want to keep only some information:
{id: 268592,
name: 'The 100',
status: 'Continuing',
seasons: [1,2,3,4,5,6],
episodes: [
{season: 1,
seasonEpisodes: [ {id: 3, etc: 'etc.'} ]
}
}
}
The solution I've succeeded with the help of #Chris G (thanks very much!)
return {
season: Number(seasonNumber),
seasonEpisodes: seasonEpisodesArray
.filter((episodeNumber) => {
return (
tvshow.episodes[seasonNumber][episodeNumber].airdate <
currentDate
);
})
.map((episodeNumber) => {
return {
episodeNumber: Number(episodeNumber),
episodeName: tvshow.episodes[seasonNumber][episodeNumber].name,
episodeStatus:
tvshow.episodes[seasonNumber][episodeNumber].status,
episodeAirdate:
tvshow.episodes[seasonNumber][episodeNumber].airdate,
};
}),
};
This is what my array of logged in users looks like:
const connectedUsers = [{
user: {
uuid: 'b62-2dw',
points: 1,
},
id: "1234567"
}];
I will concat this table when a new user logs in to my system:
this.connectedUsers = [
...this.connectedUsers,
{
...payload,
id: client.id
},
];
Then my array looks like this: (I give this to better understand)
const connectedUsers = [{
user: {
uuid: 'b62-2dw',
points: 1,
},
id: "1234567"
},
{
user: {
uuid: '663-dda',
points: 5,
},
id: "33332"
}
];
If the user with the uuid like 663-dda updates his point, I perform this method again.
When I leave it as it is, something like this will be done:
const connectedUsers = [{
user: {
uuid: 'b62-2dw',
points: 1,
},
id: "1234567"
},
{
user: {
uuid: '663-dda',
points: 5,
},
id: "33332"
},
{
user: {
uuid: '663-dda',
points: 6,
},
id: "33332"
}
];
I want to write a very nice (use ES6+) algorithm that first checks if such an object exists in this array (check by id or by user.uuid). If so, update. If not, add a new object. So it should be like this:
const connectedUsers = [{
user: {
uuid: 'b62-2dw',
points: 1,
},
id: "1234567"
},
{
user: {
uuid: '663-dda',
points: 6,
},
id: "33332"
}
];
In the code where you're updating the array when a user logs in, you could do like this:
if (!this.connectedUsers.find(user => user.user.uuid === payload.user.uuid) {
this.connectedUsers = [
...this.connectedUsers,
{ ...payload, id: client.id },
];
}
const connectedUsers = [{
user: {
uuid: 'b62-2dw',
points: 1,
},
id: "1234567"
},
{
user: {
uuid: '663-dda',
points: 5,
},
id: "33332"
},
{
user: {
uuid: '663-dda',
points: 6,
},
id: "33332"
}];
//let take some sample object
const newObDuplicateUuid = {
user: {
uuid: '663-dda',
points: 6,
},
id: "3333290"
}
const newObDuplicateId = {
user: {
uuid: '756-dda',
points: 6,
},
id: "33332"
}
const newObFresh = {
user: {
uuid: '756-dda',
points: 6,
},
id: "3333290"
}
let checkRule = connectedUsers.every(item => item.user.uuid != newObDuplicateUuid.user.uuid && item.id != newObDuplicateUuid.id)
//return false for same uuid
checkRule = connectedUsers.every(item => item.user.uuid != newObDuplicateId.user.uuid && item.id != newObDuplicateId.id)
//return false for same id
checkRule = connectedUsers.every(item => item.user.uuid != newObFresh.user.uuid && item.id != newObFresh.id)
//return true
console.log('Passed validation :'+checkRule);
const result = checkRule ? [...connectedUsers,newObFresh] : 'There is duplicate value';
console.log(result);
This is my approach. One function that covers both cases. Just pass the array and the new entry and it will return the updated list
function updateOrAddUser(listOfUsers, newEntry) {
let found = false;
const updatedUserList = listOfUsers.map(entry => {
if (entry.user.uuid === newEntry.user.uuid) {
found = true;
return newEntry;
}
return entry;
});
if (!found) {
updatedUserList.push(newUser);
}
return updatedUserList;
}
How to update the array by changing one of the objects?
This will be my array code
this.datas = [
{
index: 1,
name: 'Tony',
status: 'Absent',
reason: null
},
{
index: 2,
name: 'Chris',
status: 'Present',
reason: null
},
];
So now i want to write a function to update the reason like {reason: any reason} which from the index 1, index 2 remain the same.
So Far i had tried these
setReason = reason => {
let data = [...this.state.nameList];
let ind = data.findIndex(el => el.index === 'number');
data[ind] = { ...data[ind], reason: reason };
this.setState({
nameList: data
});
};
To update a single object data of an array
You need to first know the index of the object in the array which you want to update
eg Want to update first object of array
this.datas[0].reason = 'My custom reason';
Now you want to update the object of an array by finding the object in array you have to add a loop in it
eg: Update reason where name = Chris
for(i=0; i<this.datas.length; i++){
if(this.datas[i].name=='Chris'){
this.datas[i].reason = 'My custom reason';
break;
}
}
you can use map function on array.
let data = [
{
index: 1,
name: 'Tony',
status: 'Absent',
reason: null
},
{
index: 2,
name: 'Chris',
status: 'Present',
reason: null
},
];
data = data.map(obj=>{
return{
...obj,
reason: 'any reason',
}
})
You can use of in the for loop and update the property value in an array, this is the working solution of your question.
var datas = [
{
index: 1,
name: 'Tony',
status: 'Absent',
reason: null
},
{
index: 2,
name: 'Chris',
status: 'Present',
reason: null
},
];
for(data of datas){
data["reason"] = "any reason";
}
console.log(datas);
Try this
var datas = [
{
index: 1,
name: 'Tony',
status: 'Absent',
reason: null
},
{
index: 2,
name: 'Chris',
status: 'Present',
reason: null
},
];
datas.map(data=> data.reason ="my reason");
console.log(datas)