Map array of objects into new groups - javascript

I have an object with two arrays of objects being returned from an API. I need to try to map this into a new array of objects so I can group the data for the Vue v-select component. CodePen Example
fields: {
current: [
{
name: 'Date'
},
{
name: 'Time'
}
],
previous: [
{
name: 'Birthday'
},
{
name: 'Comments'
}
]
},
How can I map this into a new array of objects that looks like this?
grouped: [
{
group: "Current",
},
{
name: "Date"
},
{
name: "Time"
},
{
group: "Previous"
},
{
name: "Birthday"
},
{
name: "Comments"
},
]

Use Object.entries() to get the groups, and their values, and map them using Array.flatMap(). Create the group's object, and add it to an array with the group's items.
const flattenGroups = fields =>
Object.entries(fields)
.flatMap(([group, items]) => [{ group }, ...items])
const fields = {"current":[{"name":"Date"},{"name":"Time"}],"previous":[{"name":"Birthday"},{"name":"Comments"}]}
const result = flattenGroups(fields)
console.log(result)

Related

how to add element to object in array javascript

I have an array of objects and it is structured as so:
let array1 = [{}]
array1.push({
title: 'myTitle1',
extendedProps: {
field1: 'hello'
}
})
now what I am trying is do an axios.get request and use the data returned to add a new element into array1. I am successfully retrieving the data from the axios request, however, when I try to push the data: res.data[i]._doc.userIsReg, like so:
array1.push({
...array1,
extendedProps: {
...array1.extendedProps,
userIsReg: true
}
})
as you can see, I am using the spread functionaluty to include the current data from array1 into the array and then I try to append a new element userIsReg to the object extendedProps. Now, I assumed this would work, however, when I do this, it creates new object entries within the array and includes everything from inside the array currently (from spread functionality) and adds new entries with the userIsReg in there.
so to be more clear, I start with this:
[{
title: 'myTitle1',
extendedProps: {
field1: 'hello'
}
},
{
title: 'myTitle2',
extendedProps: {
field1: 'hello2'
}
},
]
and then once i do the array1.push with the spread functionality, i get this:
[{
title: 'myTitle1',
extendedProps: {
field1: 'hello'
}
},
{
title: 'myTitle2',
extendedProps: {
field1: 'hello2'
}
},
{
title: 'myTitle1',
extendedProps: {
field1: 'hello',
userIsReg: true
}
},
{
title: 'myTitle2',
extendedProps: {
field1: 'hello2',
userIsReg: true
}
},
]
so basically doubles everything up, instead of appending it to the current array. how would i fix this?
You can do something like following once you have response
array1.map(element => ({...element, userIsReg: true}))
It will add userIsReg flag to each object of your array.
Now as you want it inside extendedProps, you can use following
array1.map(element => (
{...element,
extendedProps: {
...element.extendedProps,
userIsReg: true
}}))
as you defined your array in let type you can do it in this way:
array1 = [...array1.filter(item=> item.title === selected_item.title ),{
...selected_item,userIsReg: true
}]
what I did is to just remove the previous element and add a new one with a new value
if you want to preserve order of array you can sort that
Try this way :
let array1 = [{
title: 'myTitle1',
extendedProps: {
field1: 'hello'
}
},
{
title: 'myTitle2',
extendedProps: {
field1: 'hello2'
}
}];
array1.forEach(obj => {
obj.extendedProps['userIsReg'] = true // this value is coming from API
});
console.log(array1);

Filtering and maping in nested arrays

My task is to filter objects by values in nested arrays. like in example below:
const array = [
{
authorId: '62222a1cea00a0601f200142',
description: [
[
{
title: 'English description',
paragraph: 'And english paragraph!!!!',
},
],
[
{
title: 'some title!!!',
paragraph: 'some para!!',
},
],
],
removed: false,
status: 'NEW',
},
{
authorId: '621f97562511255efa0f135e',
description: [
[
{
title: 'EEEE',
paragraph: 'aaaa',
},
],
],
removed: false,
status: 'NEW',
},
{
description: [
[
{
title: 'TEST',
paragraph: 'TESR',
},
],
],
removed: false,
status: 'NEW',
},
{
authorId: '621f97432511255efa0f135c',
description: [
[
{
title: 'My task',
paragraph: 'Parapgraph 19',
},
],
],
removed: false,
status: 'NEW',
},
]
my expected results is something like that, based on search input, which is conts searchInput
const searchInput = "par"
const array = [
{
authorId: '62222a1200142',
description: [
[
{
title: 'English description',
paragraph: 'And english paragraph!!!!',
},
],
[
{
title: 'some title!!!',
paragraph: 'some para!!',
},
],
],
removed: false,
status: 'NEW',
},
{
authorId: '6a000142',
description: [
[
{
title: 'TEST',
paragraph: 'paragraph one',
},
],
],
removed: false,
status: 'NEW',
},
]
Ive already try something like this:
const results = array?.map((el) => el?.description.map((i) => i.map((item) => item.paragraph))).filter((description) =>description.toString().toLowerCase().includes(searchValue))
But it return only arrays with paragraphs and i expected to filter whole objects, with all data, not only strings
You need to put the map inside the filter, so your data isn't modified. You use first map the description paragraphs into an array and check if all the values of the paragraphs includes the searched param.
const searchValue = 'para';
const array = [{authorId: '62222a1cea00a0601f200142',description: [[{title: 'English description',paragraph: 'And english paragraph!!!!',},],[{title: 'some title!!!',paragraph: 'some para!!',},],],removed: false,status: 'NEW',},{authorId: '621f97562511255efa0f135e',description: [[{title: 'EEEE',paragraph: 'aaaa',},],],removed: false,status: 'NEW',},{description: [[{title: 'TEST',paragraph: 'TESR',},],],removed: false,status: 'NEW',},{authorId: '621f97432511255efa0f135c',description: [[{title: 'My task',paragraph: 'Parapgraph 19',},],],removed: false,status: 'NEW',},];
const results = array.filter(el => {
return el.description
.map(i => {
return i.map(item => item.paragraph)
})
.every((description) => {
return description.toString().toLowerCase().includes(searchValue)
})
});
console.log(results);
I didn't tested the function, however it should work. Put the questionmarks when needed.
I think the filter should be on the array itself, since that is what you expect to be the result.
I used the some function to resolve the arrays in the object.
array?.filter(el=>el?.description.some(el2=>el2.some(el3=>el3?.paragraph.toString().toLowerCase().includes(searchValue))))
It's easier to understand if you don't have everything on one line.
Use filter to return a new array of objects where the description (inner) array has an at leasr one object that contains a paragraph containing the query.
const array=[{authorId:"62222a1cea00a0601f200142",description:[[{title:"English description",paragraph:"And english paragraph!!!!"}],[{title:"some title!!!",paragraph:"some para!!"}]],removed:!1,status:"NEW"},{authorId:"621f97562511255efa0f135e",description:[[{title:"EEEE",paragraph:"aaaa"}]],removed:!1,status:"NEW"},{description:[[{title:"TEST",paragraph:"TESR"}]],removed:!1,status:"NEW"},{authorId:"621f97432511255efa0f135c",description:[[{title:"My task",paragraph:"Parapgraph 19"}]],removed:!1,status:"NEW"}];
const query = 'par';
const out = array.filter(outer => {
// Return an object when the inner array
// of the function has some object that
// contains a paragraph containing the query
return outer.description.some(arr => {
return arr.some(inner => {
return inner.paragraph
.toLowerCase()
.includes(query);
});
});
});
console.log(out);
Additional documentation
some
have you tried .reduce()? It is combination of .map() and .filter() as you can see here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
Takes some time to get into .reduce(), but once you get it, you will like it!

Creating filtered and simplified data from deeply nested array of objects

I have somewhat complex set of data that I would need to simplify and split into two different sets, based on a comparison on nested object property value.
Here's the data structure I'm working with:
[
{
object: { id: 123 },
group: [
{
events: [
{
type: 'type',
schedule: [
{
time: '2019-10-30T09:30:00+00:00',
},
{
time: '2019-10-21T21:00:00+00:00',
}
],
},
{
type: 'type',
schedule: [
{
time: '2019-10-24T09:30:00+00:00',
},
{
time: '2019-09-22T21:00:00+00:00',
},
],
},
],
},
],
},
]
Most important thing would be to split this data into two different sets (arrays of objects), based on whatever or not the schedule value of time is in the past or in the future. It would also help to simplify the structure as a whole. There is a lot of properties attached to the objects and I'm fine brining in the whole object instead of just cherry-picking the important ones, as long as the nesting is logical and usable.
So something like this for the "past" data would be ideal:
[
{
id: 123,
events: [
{
type: 'type',
schedule: [
{
time: '2019-10-21T21:00:00+00:00',
}
],
},
{
type: 'type',
schedule: [
{
time: '2019-09-22T21:00:00+00:00',
}
],
}
]
}
]
I've been trying to use the different array methods (filter and map) to spit out something suitable for my needs, but can't figure out how to do it right. Mostly how to filter based on a nested value and copy over all the nesting structure on a match.
You'll need map() to restructure your array, spread syntax to iterate over object properties in-place, and filter() to filter the data based on a condition.
Using the code below, you can get an array of all past events. You can modify the condition accordingly to get all future events.
// mapping from old data array
const pastData = data.map(({object, group}) => ({
// get all properties of the nested "object"
...object,
// map "events" into a new structure
events: group[0].events.map((event) => (
{
// get all other properties
...event,
// include only those "schedule" objects where time is less than current date
schedule: event.schedule.filter(schedule => new Date(schedule.time) < new Date()),
}
)),
}));
Here's a working example:
const data = [
{
object: { id: 123 },
group: [
{
events: [
{
type: 'type',
schedule: [
{
time: '2019-10-30T09:30:00+00:00',
},
{
time: '2020-10-21T21:00:00+00:00',
}
],
},
{
type: 'type',
schedule: [
{
time: '2019-10-24T09:30:00+00:00',
},
{
time: '2020-09-22T21:00:00+00:00',
},
],
},
],
},
],
},
];
const pastData = data.map(({object, group}) => ({
...object,
events: group[0].events.map((event) => (
{
...event,
schedule: event.schedule.filter(schedule => new Date(schedule.time) < new Date()),
}
)),
}));
const futureData = data.map(({object, group}) => ({
...object,
events: group[0].events.map((event) => (
{
...event,
schedule: event.schedule.filter(schedule => new Date(schedule.time) >= new Date())
}
))
}));
console.log('past data:', pastData, ', future data:', futureData);
Try to use map method and spread operator:
const result = data.map(({object, group}) =>
({ ...object, events: group.map(g=> g.events)}));
const data =
[
{
object: { id: 123 },
group: [
{
events: [
{
type: 'type',
schedule: [
{
time: '2019-10-30T09:30:00+00:00',
},
{
time: '2019-10-21T21:00:00+00:00',
}
],
},
{
type: 'type',
schedule: [
{
time: '2019-10-24T09:30:00+00:00',
},
{
time: '2019-09-22T21:00:00+00:00',
},
],
},
],
},
],
},
];
const result = data.map(({object, group}) => ({ ...object, events: group.map(g=> g.events)}));
console.log(result)

lodash find property in nested array not working

i have an array like so
sections: [
{
editing: false,
id: 1234,
rows: [
{
editing: false,
id: 3435,
columns: [
{
id: 1535,
elements: [
{
editing: true,
id: 4849
}
]
}
]
}
]
},
]
and im trying to find any object with a property editing is true.
the following code works, but only for sections and rows, but for some reason its not finding the property in the elements array
this is the js, using lodash
return _(state.sections)
.thru(function(coll) {
return _.union(coll, _.map(coll, 'rows'));
})
.thru(function(coll2) {
return _.union(coll2, _.map(coll2, 'columns'));
})
.thru(function(coll3) {
return _.union(coll3, _.map(coll3, 'elements'));
})
.flatten()
.find({ editing: true });
After the first thru the intermediate result of the chain is an array consisting of an object and an array:
[
{ id: 1234, .... },
[ { id: 3435, ... } ]
]
To get what you want, replace the map calls with flatMap which would then returns this after the first thru:
[
{ id: 1234, .... },
{ id: 3435, ... }
]
As there will be undefined returned in the intermediate results if the objects don't have columns or elements, you will need to use compact to remove these before performing the find:
return _(state.sections)
.thru(function(coll) {
return _.union(coll, _.flatMap(coll, 'rows'));
})
.thru(function(coll2) {
return _.union(coll2, _.flatMap(coll2, 'columns'));
})
.thru(function(coll3) {
return _.union(coll3, _.flatMap(coll3, 'elements'));
})
.compact()
.find({ editing: true });

Ramda.js Combine two array of objects that share the same property ID

I have these two array of objects
todos: [
{
id: 1,
name: 'customerReport',
label: 'Report send to customer'
},
{
id: 2,
name: 'handover',
label: 'Handover (in CRM)'
},
]
And:
todosMoreDetails: [
{
id: 1,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: []
},
{
id: 2,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: []
}
]
So that the final array of objects will be a combination of the two, based on the object ID, like below:
FinalTodos: [
{
id: 1,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: [],
name: 'customerReport',
label: 'Report send to customer'
},
{
id: 2,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: [],
name: 'handover',
label: 'Handover (in CRM)'
}
]
I tried with merge mergeAll and mergeWithKey but I am probably missing something
You can achieve this with an intermediate groupBy:
Transform the todosMoreDetails array into an object keyed by todo property ID using groupBy:
var moreDetailsById = R.groupBy(R.prop('id'), todosMoreDetails);
moreDetailsById is an object where the key is id, and the value is an array of todos. If the id is unique, this will be a singleton array:
{
1: [{
id: 1,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: []
}]
}
Now transform the todos array by merging each todo to it's details you retrieve from the grouped view:
var finalTodos = R.map(todo => R.merge(todo, moreDetailsById[todo.id][0]), todos);
An alternate more detailed way:
function mergeTodo(todo) {
var details = moreDetailsById[todo.id][0]; // this is not null safe
var finalTodo = R.merge(todo, details);
return finalTodo;
}
var moreDetailsById = R.groupBy(R.prop('id'), todosMoreDetails);
var finalTodos = todos.map(mergeTodo);
I guess merge is only used for arrays. Have a search for object "extend". Maybe storing the todo details not in seperate objects is the better solution.
Using jQuery? https://api.jquery.com/jquery.extend/
Using underscore? http://underscorejs.org/#extend
Native approach? https://gomakethings.com/vanilla-javascript-version-of-jquery-extend/
Using underscore:
var result = [];
var entry = {};
_.each(todos, function(todo) {
_.each(todosMoreDetails, function(detail) {
if (todo.id == detail.id) {
entry = _.extend(todo, detail);
result.push(entry);
}
}
});
return result;

Categories