ReactJS - Filter Nested Array of Objects - javascript

So I am trying to set up a filter on nested array of objects. The thing is the filter is object.
I just got the empty result.
Data sample:
obj:[
{
"success": true,
"data": {
"purpose": {
"label": "Purpose",
"type": "choice",
"value": {
"select": "loremIpsum",
"options": [
{
"value": "loremIpsum",
"text": "loremIpsum"
}
]
},
"filter_groups": [
"sd",
"coll"
],
"checklist_groups": {
"sd": {
"value": null,
"comment": [],
"is_disabled": false
}
}
},
"description": {
"label": "Description",
"type": "choice",
"value": {
"select": "loremIpsum",
"options": [
{
"value": "loremIpsum",
"text": "loremIpsum"
}
]
},
"filter_groups": [
"dv"
],
"checklist_groups": {
"sd": {
"value": null,
"comment": [],
"is_disabled": false
}
}
}
}
}
]
Try to do next:
const filterData = (e) => {
let filtredData = Object.entries(obj)
.map(item => ({
...item,
filter_groups: item.filter_groups
.filter(child => child === e)
}))
return filtredData;
}
But filter wants work, how filter by nested objects?
I want to filter by filter_groups.
Do I have to use a regex to define the filter?

It's really not entirely clear what exactly you want as input and how your output should be formatted. This version assumes you just want the data property of the first value of obj's array, and turns it back into an object with the same sorts of keys you had in the original (such as "purpose" or "description").
const filterData = (e) => (data) => Object .fromEntries (
Object .entries (data) .filter (([k, {filter_groups}]) => filter_groups .includes (e))
)
const obj = [{success: !0, data: {purpose: {label: "Purpose", type: "choice", value: {select: "loremIpsum", options: [{value: "loremIpsum", text: "loremIpsum"}]}, filter_groups: ["sd", "coll"], checklist_groups: {sd: {value: null, comment: [], is_disabled: !1}}}, description: {label: "Description", type: "choice", value: {select: "loremIpsum", options: [{value: "loremIpsum", text: "loremIpsum"}]}, filter_groups: ["dv"], checklist_groups: {sd: {value: null, comment: [], is_disabled: !1}}}}}]
console .log ('"sd": ', filterData ('sd') (obj [0] .data))
console .log ('"dv": ', filterData ('dv') (obj [0] .data))
console .log ('"foobar": ', filterData ('foobar') (obj [0] .data))
.as-console-wrapper {max-height: 100% !important; top: 0}
Forgetting that outer shell, the trick here is that we convert it to an array of key-value entries with Object .entries, filter those entries by whether the filter_groups property of the value contains our target value, and convert the resulting array of entries back into an object.

Try using a predicate method to perform a boolean test and determine it obj:[] is behaving live an array
```Array.isArray(arr); // true ```

Related

Array of object into a nested object for every value in the array

Trying to turn an array of objects into a nested object. Is there a good method for this? and how do I make it depending on the array length?
Working but is not universal:
https://codesandbox.io/s/thirsty-roentgen-3mdcjv?file=/src/App.js
What I have:
sorting: [
{
"id": "HighestDegree",
"options": [
"HighSchool",
"Undergraduate",
"Bachelor",
"Master",
"Doctor"
]
},
{
"id": "gender",
"options": [
"male",
"female"
]
}
]
What I want:
value: {
"Region": "Oklahoma",
"HighestDegree": {
"HighSchool": {
"male": null,
"female":null
},
"Undergraduate":{
"male": null,
"female":null
}
//and so on...
}
}
The code beneath works but is hardcoded for only two different options. I want it to be able to nest the length of the array. So lets say another object was age it would be {"HighSchool":{male:{"<25":null,"25-35":null}}} etc..
function testSortingArray() {
let sorting = [
{
id: "HighestDegree",
options: ["HighSchool", "Undergraduate", "Bachelor", "Master", "Doctor"]
},
{
id: "gender",
options: ["male", "female"]
}
];
let GoalArray = {};
if (sorting.length > 0) {
sorting[0].options.map((firstArray) => {
let currObject = {};
sorting[1].options.map((secondOption) => {
currObject[secondOption] = null;
});
GoalArray[firstArray] = currObject;
});
}
return GoalArray;
}
console.log(testSortingArray());
You can do it with a recursive function.
The function below reduces every options array to an object, and then continues populating that object if there are rest elements left from the original sorting array.
const fn = ([{ options }, ...rest]) => options.reduce((a, v) => ({
...a,
[v]: rest.length ? fn(rest): null
}), {});
const result = fn(sorting);
Besides the reduce() method, the code above makes use of object and array destructuring and spread syntax.
Complete snippet:
const sorting = [{
"id": "HighestDegree",
"options": [
"HighSchool",
"Undergraduate",
"Bachelor",
"Master",
"Doctor"
]
}, {
"id": "gender",
"options": [
"male",
"female"
]
}, {
"id": "age",
"options": [
"<25",
"25-35"
]
}];
const fn = ([{ options }, ...rest]) => options.reduce((a, v) => ({
...a,
[v]: rest.length ? fn(rest): null
}), {});
const result = fn(sorting);
console.log(result);

Insert new JSON objects in nested JS array based on condition

For one of my e-commerce application requirement, I have a nested array of the form (Sample):
const data = [
{
"id": 1,
"group": "upper-wear",
"labels": [
{
"type": "shirts",
"quantity": "20",
},
],
popular: true
},
{
"id": 2,
"group": "bottom-wear",
"lables": [
{
"type": "trousers",
"quantity": "31",
},
],
popular: true
},
]
To this array, I need to insert new objects to the array 'labels' if the group value equals 'upper-wear'.
const newDataToInsert = [
{
"type": 'blazers',
"quantity": 19
},
]
This is what I tried so far, considering that for now I only need to insert to single label (i.e. 'upper-wear') (in future, there can be multiple labels category 'upper-wear', 'bottom-wear', to be inserted into):
const updatedArray = data.map((datum) => {
if (datum.group === 'upper-wear') {
return {
...datum,
labels: [...datum.labels, ...newDataToInsert]
};
}
});
console.log(updatedArray);
But there seems to be a silly issue that I am missing as the result returns like this:
[
{
id: 1,
group: 'upper-wear',
labels: [ [Object], [Object] ],
popular: true
},
undefined
]
I know there may be better approaches available, but this is what I can think of as the minimum solution for now.
any help to resolve the current or any better solution will be highly appreciated.
Try with this
updatedArray = data.map((d) => {
if (d.group && d.group === 'upper-wear') {
return { ...d, labels: d.labels.concat(newDataToInsert) }
} else {
return d;
}
})
const data = [
{
"id": 1,
"group": "upper-wear",
"labels": [
{
"type": "shirts",
"quantity": "20",
},
],
popular: true
},
{
"id": 2,
"group": "bottom-wear",
"lables": [
{
"type": "trousers",
"quantity": "31",
},
],
popular: true
},
];
const newDataToInsert = [
{
"type": 'blazers',
"quantity": 19
},
];
const updatedArray = data.map((d) => {
if (d.group && d.group === 'upper-wear') {
return { ...d, labels: d.labels.concat(newDataToInsert) }
} else {
return d;
}
});
console.log(updatedArray)
Explaination
Here while mapping the data, we check for the condition
IF
If it matches then we will first copy the whole object from the variable b return { ...b }
after that we take another variable with the same name lables return { ...d, labels: d.labels.concat(newDataToInsert) },As per the JSON default nature the new variable with the same name will hold the latest value
Here in labels we first take a copy of old data and then merge it with newDataToInsert array labels: d.labels.concat(newDataToInsert), It will merge 2 arrays and store them in JSON with the name labels
Else
In else we just return the current values else { return d; }
You don't actually need to iterate with map over the array. Just find an object in the array and change what you want.
const data=[{id:1,group:"upper-wear",labels:[{type:"shirts",quantity:"20"}],popular:true},{id:2,group:"bottom-wear",lables:[{type:"trousers",quantity:"31"}],popular:true}];
const newDataToInsert=[{type:"blazers",quantity:19}];
data.find(({ group }) => group === 'upper-wear')?.labels.push(...newDataToInsert);
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You're not returning all objects from your map. you're only returning a result when your criteria is met. This is resulting in your undefined objects...
const data = [
{ "id": 1, "group": "upper-wear", "labels": [ { "type": "shirts", "quantity": "20", }, ], popular: true },
{ "id": 2, "group": "bottom-wear", "lables": [ { "type": "trousers", "quantity": "31", }, ], popular: true },
]
const newDataToInsert = [ { "type": 'blazers',"quantity": 19 }, ]
const updatedArray = data.map(datum => {
if (datum.group === 'upper-wear') datum.labels = [...datum.labels, ...newDataToInsert]
return datum
});
console.log(updatedArray);
You can use Array#find to locate the desired group and then change labels for the group found. There are two options depending on how many items you would like to insert. Use Array#push to add the desired item; use forEach for more than one item:
const searchgroup = "upper-wear";
const target = data.find(({group}) => group === searchgroup);
target.labels.push(...newDataToInsert); //For one item to insert
//newDataToInsert.forEach(label => target.labels.push( label )); //For more than one item
const data = [{"id": 1, "group": "upper-wear", "labels": [{"type": "shirts", "quantity": "20"},],popular: true }, {"id": 2, "group": "bottom-wear", "lables": [{"type": "trousers", "quantity": "31", },],popular: true}];
const newDataToInsert = [{"type": 'blazers', "quantity": 19}];
//group to find
const searchgroup = "upper-wear";
//target element in data
const target = data.find(({group}) => group === searchgroup);
//check if group was found
if( target ) {
//if there's only one product in newDataToInsert us this:
//target.labels.push(...newDataToInsert);
//if you have more than one product to be inserted use this; also works for one
newDataToInsert.forEach(label => target.labels.push( label ));
} else {
console.log( `No such group found: ${searchgroup}!` );
}
console.log( data );

Flatten a deeply nested data structure of arrays, objects + strings into a list of data items while mapping the former parent-child relationship too

Restructuring array of objects to new array
Problem
There’s an array of objects that contains plain strings and might contain nested arrays as well. We want to create a new Array that will contain a node for each item in the array and separate nodes for each array item connected to its parent. Each parent node should have the following structure:
{
id: uuidv4(),
position: { x: 0, y: 0 },
data: { label: <item data goes here> }
}
Each array node with the following schema above, should also have a connection edge item added to the array with the following properties:
{
id: ‘e<array item Id>-<parentId>’,
source: <array item Id>,
target: <parentId>,
}
Example
We have the following array of objects for example:
[
{
"author": "John Doe",
"age": 26,
"books": [
{
"title": "Book 1"
},
{
"title": "Book 2",
"chapters": [
{
"title": "No Way Home",
"page": 256
}
]
}
]
}
]
The expected output is:
[
{
"id": "1",
"data": {
"label": {
"author": "John Doe",
"age": 26,
}
}
},
{
"id": "2",
"data": {
"label": "books" // key of array
}
},
{
"id": "3",
"data": {
"label": {
"title": "Book 1"
}
}
},
{
"id": "4",
"data": {
"label": {
"title": "Book 2"
}
}
},
{
"id": "5",
"data": {
"label": "chapters" // key of array
}
},
{
"id": "6",
"data": {
"label": {
"title": "No Way Home",
"page": 256
}
}
},
{
"id": "e2-1",
"source": "2",
"target": "1"
},
{
"id": "e3-2",
"source": "3",
"target": "2"
},
{
"id": "e4-2",
"source": "4",
"target": "2"
},
{
"id": "e5-4",
"source": "5",
"target": "4"
},
{
"id": "e6-5",
"source": "6",
"target": "5"
}
]
First of all, I would not be answering if there was not already a good answer. Please, on StackOverflow, always show your own attempts and explain where you got stuck. But since there is already an answer, I think this version might be a bit simpler.
Second, I'm assuming this output format is some sort of directed graph, that the first half is your list of vertices and the second half a list of edges. If so I don't know if your output format is constrained here. But if you had the option, I would think a better structure would be an object with vertices and edges properties, each containing an array. You might then not need the edges' ids. And the code could also be simplified.
This version first converts to an intermediate structure like this:
[
{id: "1", data: {label: {author: "John Doe", age: 26}}, children: [
{id: "2", data: {label: "books"}, children: [
{id: "3", data: {label: {title: "Book 1"}}, children: []},
{id: "4", data: {label: {title: "Book 2"}}, children: [
{id: "5", data: {label: "chapters"}, children: [
{id: "6", data: {label: {title: "No Way Home"}}, children: []}
]}
]}
]}
]}
]
Then we flatten that structure into the first section of the output and use it to calculate the relationships (edges?) between nested nodes to go in the second section.
The code looks like this:
const transform = (input) => {
const extract = (os, nextId = ((id) => () => String (++ id)) (0)) => os .map ((o) => ({
id: nextId(),
data: {label: Object .fromEntries (Object .entries (o) .filter (([k, v]) => !Array .isArray (v)))},
children: Object .entries (o) .filter (([k, v]) => Array .isArray (v)) .flatMap (([k, v]) => [
{id: nextId(), data: {label: k}, children: extract (v, nextId)},
])
}))
const relationships = (xs) =>
xs .flatMap (({id: target, children = []}) => [
... children .map (({id: source}) => ({id: `e${source}-${target}`, source, target})),
... relationships (children),
])
const flatten = (xs) =>
xs .flatMap (({children, ...rest}) => [rest, ... flatten (children)])
const res = extract (input)
return [...flatten (res), ... relationships (res)]
}
const input = [{author: "John Doe", age : 26, books: [{title: "Book 1"}, {title: "Book 2", chapters: [{title: "No Way Home", page: 256}]}]}]
console .log (transform (input))
.as-console-wrapper {max-height: 100% !important; top: 0}
We use three separate recursive functions. One does the recursive extract into that intermediate format. Along the way, it adds id nodes using a nextId stateful function (something I usually avoid, but seems to simplify things here.) Then flatten simply recursively lifts the children to sit alongside their parents. And relationships (again recursively) uses the ids of the parent- and child-nodes to add an edge node.
Using these three separate recursive calls is probably less efficient than some other solutions, but I think it leads to much cleaner code.
One has to choose a self recursive approach which in a generic way can process both, array-items and object-entries. Also, while the recursive process takes place, one not only has to create and collect the consecutively/serially numbered (the incremented id value) data nodes, but one in addition needs to keep track of every data node's parent reference in order to finally concatenate the list of edge items (as the OP calls it) to the list of data nodes.
function flattenStructureRecursively(source = [], result = [], tracker = {}) {
let {
parent = null, edgeItems = [],
getId = (id => (() => ++id))(0),
} = tracker;
const createEdgeItem = (id, pid) => ({
id: `e${ id }-${ pid }`,
source: id,
target: pid,
});
const putNodeData = node => {
result.push(node);
if (parent !== null) {
edgeItems.push(createEdgeItem(node.id, parent.id));
}
// every data node is a parent entity too.
parent = node;
};
if (Array.isArray(source)) {
result.push(
...source.flatMap(item =>
flattenStructureRecursively(item, [], {
getId, parent, edgeItems,
})
)
);
} else {
let {
dataNode,
childEntries,
} = Object
.entries(source)
.reduce(({ dataNode, childEntries }, [key, value]) => {
if (value && (Array.isArray(value) || (typeof value === 'object'))) {
// collect any object's iterable properties.
childEntries.push([key, value]);
} else {
// aggregate any object's non iterable
// properties at data node level.
(dataNode ??= {
id: getId(),
data: { label: {} }
}).data.label[key] = value;
}
return { dataNode, childEntries };
}, { dataNode: null, childEntries: [] });
if (dataNode !== null) {
putNodeData(dataNode);
}
childEntries
.forEach(([key, value]) => {
// every object's iterable property is supposed
// to be created as an own parent entity.
dataNode = {
id: getId(),
data: { label: key },
};
putNodeData(dataNode);
result.push(
...flattenStructureRecursively(value, [], {
getId, parent, edgeItems,
})
);
});
}
if (parent === null) {
// append all additionally collected edge items
// in the end of all the recursion.
result.push(...edgeItems);
}
return result;
}
console.log(
flattenStructureRecursively([{
author: "John Doe",
pseudonym: "J.D.",
books: [{
title: "Book 1",
}, {
title: "Book 2",
chapters: [{
title: "No Way Home",
page: 256,
}],
}],
age: 26,
}])
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

How will I tranform nested json into flatten json using underscore js

{ "data": [ { "appointment": "string", "archived": true, "clinical-sections": [ { "clinical-template": 0,
"name": "string", "values": [ { "clinical-field": 0, "id": 0, "value": "string" } ] } ] } ] }
This is the json response and my goal is to transform /flatten this json into flatten json object using underscore js.
Desire output should look like this
{
"data": [
{
"appointment": "string",
"archived": true,
"clinical_note_template": 0,
"name": "string",
"clinical_note_field": 0,
"id": 0,
"value": "string"
]
}
var result = [];
_.each(response.data, function(element) { _.each(clinical_note_sections, function(aitem) { var bitem = _.extend({ appointment: element.appointment }, aitem); result.push(bitem); }); });
return result;
The above function should return
{
"data": [
{
"appointment": "string",
"clinical-note-template": 0
}
]
}
But It is not working and I have no idea hot to get the desire resut
While you could do something with appropriate parameter destructuring like this:
const convert = ({data, ...rest}) => ({
...rest,
data: data.map(({
appointment,
archived,
['clinical-sections']: [{
['clinical-template']: clinical_note_template,
name,
values: [{
['clinical-field']: clinical_note_field,
id,
value
}]
}]
}) => ({appointment, archived, clinical_note_template, name, clinical_note_field, id, value}))
})
const response = {data: [{appointment: "string", archived: true, "clinical-sections": [{"clinical-template": 0, name: "string", values: [{"clinical-field": 0, id: 0, value: "string"}]}]}]}
console .log (
convert (response)
)
.as-console-wrapper {max-height: 100% !important; top: 0}
I suspect there is more going on than we've seen so far.
What happens if there are multiple elements in the clinical-sections array? How about in the values array inside of that? Which values then become your clinical_note_template and clinical_note_field?
But in any case, this might be a start.

combining a names array with the values array in object es6

I'm using the influxdata API and that data comes back in this format:
{
"columns": [
"time",
"commit_time",
"expiration_time",
"kafka_consumer_group",
"kafka_offset_group",
"kafka_topic",
"kafka_topic_parition",
"message_metadata"
],
"name": "offsets",
"values": [
[
"2017-04-01T22:51:50.301Z",
"1491087110301",
"1491173510301",
"group-general-service-master",
0,
"replica-conversion-dev",
"6",
"NO_METADATA"
],
[
"2017-04-01T22:51:50.301Z",
"1491087110301",
"1491173510301",
"group-message-service-master",
2,
"service-dev",
"4",
"NO_METADATA"
],
[
"2017-04-01T22:51:50.303Z",
"1491087110303",
"1491173510303",
"group-general-service-mk-threadc",
7073,
"posted-dev",
"1",
"NO_METADATA"
]
]
}
The columns and values are separate but how can I combine the columns and values arrays together so that the values have the names with them:
{
[
{
"time": "2017-04-01T22:51:50.301Z",
"commit_time": "1491087110301",
"expiration_time": "1491173510301",
"kafka_consumer_group": "group-general-service-master",
"kafka_offset_group": 0,
"kafka_topic": "replica-conversion-dev",
"kafka_topic_partition": "6",
"message_metadata": "NO_METADATA"
}
]...
}
Your desired result is not a valid object/array notation.
Assuming you would like to get an array of object, you can try the following:
res.values.map(arr => ({
time: arr[0],
commit_time: arr[1],
expiration: arr[2],
// you get the idea...
}));
Or using array destructuring:
res.values.map(arr => {
var obj = {};
[
obj.time,
obj.commit_time,
obj.expiration,
// you get the idea...
] = arr;
return obj;
});
Assuming you need an array of objects, you could iterate the values array and for the keys the columns array and assign the objects to the collecting object. Then return a new array with the objects.
var data = { columns: ["time", "commit_time", "expiration_time", "kafka_consumer_group", "kafka_offset_group", "kafka_topic", "kafka_topic_parition", "message_metadata"], name: "offsets", values: [["2017-04-01T22:51:50.301Z", "1491087110301", "1491173510301", "group-general-service-master", 0, "replica-conversion-dev", "6", "NO_METADATA"], ["2017-04-01T22:51:50.301Z", "1491087110301", "1491173510301", "group-message-service-master", 2, "service-dev", "4", "NO_METADATA"], ["2017-04-01T22:51:50.303Z", "1491087110303", "1491173510303", "group-general-service-mk-threadc", 7073, "posted-dev", "1", "NO_METADATA"]] },
result = data.values.map(a => Object.assign({}, ...data.columns.map((k, i) => ({ [k]: a[i] }))));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories