lodash find property in nested array not working - javascript

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

Related

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!

Map array of objects into new groups

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)

Format a nested object

I have a javascript variable with the following structure
var recurse = {
level_0: [
{
level_1 : [
{
level_2_0 : [
{
level_3_0: {
valid: true,
available: false
}
}, {
level_3_1 : {
valid: true,
available: true
}
}]
}, {
level_2_1 : [
{
level_3_0: {
valid: true,
available: false
}
}, {
level_3_1 : {
valid: true,
available: true
}
}]
}]
}]
}
Final required output structure
var recurse = {
name: "level_0",
property: [
{
name: "level_1",
property: [
{
name: "level_2_0",
property: [
{
name: "level_3_0",
property: {
valid: true,
available: false
}
}, {
name: "level_3_1",
property: {
valid: true,
available: true
}
}]
}, {
name: "level_2_1",
property: [
{
name: "level_3_0",
property: {
valid: true,
available: false
}
}, {
name: "level_3_1",
property: {
valid: true,
available: true
}
}]
}]
}]
}
Facing problem with cloning and updating the structure for this nested object using generic methods.
How can I achieve the required final object structure using simple javascript or reactjs properties.
Which is the most appropriate method to clone a javascript object?
Note: the object names- level_0, level_1 level_2_0 could be random or dynamic.
You can write a simple recursive function that recurses on the item if the item value is an array like below
var recurse = {
level_0: [
{
level_1 : [
{
level_2_0 : [
{
level_3_0: {
valid: true,
available: false
}
}, {
level_3_1 : {
valid: true,
available: true
}
}]
}, {
level_2_1 : [
{
level_3_0: {
valid: true,
available: false
}
}, {
level_3_1 : {
valid: true,
available: true
}
}]
}]
}]
}
function format(data) {
return Object.entries(data).map(([key, value]) => {
if(Array.isArray(value))
return {
name: key,
property: [].concat(...value.map(item => format(item)))
}
return {
name: key,
property: value
}
})
}
console.log(format(recurse));
You can transform it with something like this:
const transform = (tree) =>
Object (tree) === tree
? Object .entries (tree) .map (([name, properties]) => ({
name,
properties: Array .isArray (properties) ? properties .map (transform) : properties
})) [0] // This is a strage thing to do with an object! The original data structure is odd.
: tree
var recurse = {level_0: [{level_1: [{level_2_0: [{level_3_0: {valid: true, available: false}}, {level_3_1: {valid: true, available: true}}]}, {level_2_1: [{level_3_0: {valid: true, available: false}}, {level_3_1: {valid: true, available: true}}]}]}]}
console .log (transform (recurse))
.as-console-wrapper {min-height: 100% !important; top: 0}
But the data structure you start with is quite odd. Each level of nesting except the innermost can only have a single property. Is that really how your data comes to you? It's a strange use of objects. (Then again, you are asking about how to reforat it...)
The [0] on the sixth line of the above deals with that oddity of the input.

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)

Targeting Specific Array Element with Assignment Destructuring

I am using some assignment destructuring in my MongoDB/Node backend in order to handle some post-processing. I'm just trying to understand how this destructuring works, and if, in the case of an array of multiple elements and nested arrays, if I can input the element I want to target.
Take for instance this code:
services: [
,
{
history: [...preSaveData]
}
]
} = preSaveDocObj;
My assumption is that the "," in "services" for the above code will default to looking at the first element in the array. Correct?
Now, if I have a document structure that looks like this (see below), and I know I want to target the "services" element where "service" is equal to "typeTwo", how would I do that?:
{
_id: 4d39fe8b23dac43194a7f571,
name: {
first: "Jane",
last: "Smith"
}
services: [
{
service: "typeOne",
history: [
{ _id: 121,
completed: true,
title: "rookie"
},
{ _id: 122,
completed: false,
title: "novice"
}
]
},
{
service: "typeTwo",
history: [
{ _id: 135,
completed: true,
title: "rookie"
},
{ _id: 136,
completed: false,
title: "novice"
}
]
}
]
}
How can I edit this code (see below) to specifically target the "services" array where "service" is equal to "typeTwo"?
services: [
,
{
history: [...preSaveData]
}
]
} = preSaveDocObj;
Don't overdestructure, just find:
const { history: [...preSavedData] } = doc.services.find(it => it.serice === "typeTwo");

Categories