Remove parent JSON element depending on child value - javascript

I have a JSON with lots of empty content:
{
"items": [
{
"category": "login",
"fields": [
{
"label": "Name",
"value": "",
},
{
"label": "E-Mail",
"value": "",
},
{
"label": "Password",
"value": "123456",
},
{
"label": "Website",
"fields": [
{
"label": "Name X",
"value": ""
},
{
"label": "Name Y",
"value": "another one"
},…
]
},…
]
},…
]
}
The nesting goes several levels deeper. This shows only the first level. I want to delete all elements of "fields" (or whatever the array's key in deeper levels is), where their "value" is empty.
{
"items": [
{
"category": "login",
"fields": [
{
"label": "Password",
"value": "123456",
},
{
"label": "Website",
"fields": [
{
"label": "Name Y",
"value": "another one"
},…
]
},…
]
},…
]
}
How can I do this in Javascript?

Well, I found a way to iterate through the JSON object:
function remove(jsondata) {
for (let i in jsondata) {
if (jsondata[i].value != undefined && jsondata[i].value == '') {
delete jsondata[i];
}
else if (typeof jsondata[i] === "object") remove(jsondata[i]);
}
}
Not sure, if it's the most elegant way, but it works so far.

use filter method,you could get a filtered array
it returned Boolean. if value exist,it will be true
var list=JSON.parse(data)
list.items=list.items.map(val=>{
val.fields=val.fields.filter(v=>v.value})
return val
})

We use object-scan for many data processing tasks. It's powerful once you wrap your head around it. Here is how you could answer your questions
// const objectScan = require('object-scan');
const prune = (input) => objectScan(['**[*].value'], {
rtn: 'count',
filterFn: ({ gparent, gproperty, value }) => {
if (value === '') {
gparent.splice(gproperty, 1);
return true;
}
return false;
}
})(input);
const obj = { items: [{ category: 'login', fields: [{ label: 'Name', value: '' }, { label: 'E-Mail', value: '' }, { label: 'Password', value: '123456' }, { label: 'Website', fields: [{ label: 'Name X', value: '' }, { label: 'Name Y', value: 'another one' }] }] }] };
console.log(prune(obj)); // return count of pruned entries
// => 3
console.log(obj);
// => { items: [ { category: 'login', fields: [ { label: 'Password', value: '123456' }, { label: 'Website', fields: [ { label: 'Name Y', value: 'another one' } ] } ] } ] }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#16.0.0"></script>
Disclaimer: I'm the author of object-scan

Related

Compose object from nested list

I have a generic json generated from an XML.
{
"contextInfoDTOList": [
{
"key": "context_info",
"list": [
{
"key": "composition",
"list": [
{
"key": "parts",
"list": [
{
"key": "part",
"list": [
{
"list": [
{
"key": "materials",
"list": [
{
"key": "material",
"list": [
{
"key": "material_value",
"value": "100"
},
{
"key": "material_name",
"value": "polyester"
}
]
}
]
},
{
"key": "part_name",
"value": "LINING"
}
]
},
{
"list": [
{
"key": "materials",
"list": [
{
"key": "material",
"list": [
{
"key": "material_value",
"value": "100"
},
{
"key": "material_name",
"value": "cow leather"
}
]
}
]
},
{
"key": "part_name",
"value": "OUTER SHELL"
}
]
}
]
}
]
}
]
},
{
"key": "explanation"
}
]
}
]
}
I need to extract the information to something like this:
COMPOSITION
Lining
100 % polyester
Outer Shell
100 % cow leather
I have tried a forEach approach, acceding to the keys (composition, parts, materials...), but it gets so dirty and I cannot get the materials list.
I'm considering about using a reduce to obtain an object, but I don't know how to pass an object and use recursivity through the nested list.
Desired object:
export class ContextInfo {
composition: Composition;
explanation: string;
}
export class Composition {
parts: Part[] = [];
}
export class Part {
partName: string;
materials: Material[] = [];
}
export class Material {
name: string;
value: number;
}
Any help would be appreciated. Thanks in advance!
Now it's your responsibility, to work with the data...
let contextInfoDTOList = [{ key: "context_info", list: [{ key: "composition", list: [{ key: "parts", list: [{ key: "part", list: [{ list: [{ key: "materials", list: [{ key: "material", list: [{ key: "material_value", value: "100" }, { key: "material_name", value: "polyester" }] }] }, { key: "part_name", value: "LINING" }] }, { list: [{ key: "materials", list: [{ key: "material", list: [{ key: "material_value", value: "100" }, { key: "material_name", value: "cow leather" }] }] }, { key: "part_name", value: "OUTER SHELL" }] }] }] }] }, { key: "explanation" }] }]
function* getAllKeyValue(list = [], path = []) {
for (let item of list)
if ("value" in item)
yield [item, path]
else
yield* getAllKeyValue(item.list, path.concat(item.key))
}
for (let [item, _path] of getAllKeyValue(contextInfoDTOList)) {
console.log(item);
// Todo: work with the data
}
That's a pretty ugly input format you have. But by continually filtering and finding nodes, we can build your output in a fairly reasonable manner:
const byKey = (target) => ({key}) => key == target
const extract = (res) => res .contextInfoDTOList .filter (byKey ('context_info')) .map (info => ({
explanation: info .list .find (byKey ('explanation')) .value,
composition: info .list .find (byKey ('composition')) .list .filter (byKey ('parts')) .map (parts => ({
parts: parts .list .filter (byKey ('part')) .flatMap (p => p.list .map (q => q.list)) .map (part => ({
partName: part .find (byKey ('part_name')) .value,
material: part .find (byKey ('materials')) .list .map (material => ({
name: material .list .find (byKey ('material_name')) .value,
value: material .list .find (byKey ('material_value')) .value
}))
}))
}))
}))
const res = {contextInfoDTOList: [{key: "context_info", list: [{key: "composition", list: [{key: "parts", list: [{key: "part", list: [{list: [{key: "materials", list: [{key: "material", list: [{key: "material_value", value: "100"}, {key: "material_name", value: "polyester"}]}]}, {key: "part_name", value: "LINING"}]}, {list: [{key: "materials", list: [{key: "material", list: [{key: "material_value", value: "100"}, {key: "material_name", value: "cow leather"}]}]}, {key: "part_name", value: "OUTER SHELL"}]}]}]}]}, {key: "explanation", value: "foobar"}]}]}
console .log (extract (res))
.as-console-wrapper {max-height: 100% !important; top: 0}
(Note that I had to add a dummy "value" to your "explanation" node to get it to extract properly. If we don't do this, we'd get an undefined explanation.)
We use the helper byKey simply to reduce the noise in the body of the function.

Return name and id property value of all arrays inside object

How to return name and id property value of all arrays? The idea is to make a single map of all these arrays and return the id and name?
Something like this
filters.[key].map((option, index) => (
<ItemFilter key={index}>{option}</ItemFilter>
))
I have this array object
filters: {
"services": [
{
"id": "1b975589-7111-46a4-b433-d0e3c0d7c08c",
"name": "Bank"
},
{
"id": "91d4637e-a17f-4b31-8675-c041fe06e2ad",
"name": "Income"
}
],
"accountTypes": [
{
"id": "1f34205b-2e5a-430e-982c-5673cbdb3a68",
"name": "Digital Account"
}
],
"channels": [
{
"id": "875f8350-073e-4a20-be20-38482a86892b",
"name": "Chat"
}
]
}
You can use flatMap or flat to achieve the desired result.
Object.values(obj.filters).flatMap(v => v)
or
Object.values(obj.filters).flat()
const obj = {
filters: {
services: [
{
id: "1b975589-7111-46a4-b433-d0e3c0d7c08c",
name: "Bank",
},
{
id: "91d4637e-a17f-4b31-8675-c041fe06e2ad",
name: "Income",
},
],
accountTypes: [
{
id: "1f34205b-2e5a-430e-982c-5673cbdb3a68",
name: "Digital Account",
},
],
channels: [
{
id: "875f8350-073e-4a20-be20-38482a86892b",
name: "Chat",
},
],
},
};
const result = Object.values(obj.filters).flatMap(v => v);
console.log(result);
If option is referring to name in your example code it could look something like this:
Object.values(
{
filters: {
services: [
{
id: "1b975589-7111-46a4-b433-d0e3c0d7c08c",
name: "Bank",
},
{
id: "91d4637e-a17f-4b31-8675-c041fe06e2ad",
name: "Income",
},
],
accountTypes: [
{
id: "1f34205b-2e5a-430e-982c-5673cbdb3a68",
name: "Digital Account",
},
],
channels: [
{
id: "875f8350-073e-4a20-be20-38482a86892b",
name: "Chat",
},
],
},
}.filters
)
.flat()
.map(({ name, index }) => <ItemFilter key={index}>{name}</ItemFilter>);

How to filtering array of objects to another array of objects in js?

I have got two arrays of objects. I want to filter data based on permissionObj.
This is coming from database. Here are arrays of sub-arrays in the permissionObj.
I need to do another condition in reduce function . For example , if Pubsidebar value is token is public, I want to keep static content {label: "test",value: "public"} without filtering with permissionObj and if other key and value is match with permissionObj,then it will be push inside token .
let permissionObj = [
{
'OA deal': [
{
label: 'can view',
value: 'can_view',
},
],
},
{
Deposit: [
{
label: 'can edit',
value: 'can_edit',
},
],
},
{
Deposit: [
{
label: 'can_view',
value: 'can_view',
},
],
},
{
Journals: [
{
label: 'can create',
value: 'can_create',
},
],
},
{
Dashboard: [
{
label: 'can view',
value: 'can_view',
},
],
},
{
token: [
{
label: 'can view',
value: 'can_create',
},
],
},
]
const PubSidebar = [
{
label: 'Dashboard',
value: 'can_view',
},
{
label: 'token',
value: 'public',
content: [
{
key: 'token',
value: 'can_create',
},
{
key: 'test',
value: 'public',
},
],
},
{
label: 'OA deal',
content: [
{
label: 'view oadeal',
key: 'OA deal',
value: 'can_view',
},
{
label: 'Deposit',
key: 'Deposit',
value: 'can_view',
},
{
label: 'Corrections',
key: 'Corrections',
value: 'can_edit',
},
],
},
{
label: 'Journals',
content: [
{
label: 'Add Journal',
key: 'Journals',
value: 'can_create',
},
],
},
]
const filterObject = permissionObj.reduce((a, c) => {
for (const key in c) {
a[key] = c[key]
}
return a
}, {})
const result = PubSidebar.reduce((a, c) => {
if (
filterObject[c.label] &&
c.value &&
filterObject[c.label].some(s => s.value === c.value)
) {
a.push(c)
} else if (c.value === 'public' && c.label === 'token') {
if (
(c.content = c.content.filter(
f =>
filterObject[f.key] &&
filterObject[f.key].some(s => s.value == f.value)
))
) {
c.content = c.content.filter(
f =>
filterObject[f.key] &&
filterObject[f.key].some(s => s.value == f.value)
)
a.push(c)
}
} else if (c.content.some(s => filterObject[s.key]) && c.content) {
c.content = c.content.filter(
f =>
filterObject[f.key] && filterObject[f.key].some(s => s.value == f.value)
)
a.push(c)
}
return a
}, [])
console.log(result)
Here is code snippet . I am trying to getting public data from sidebar without filtering with permissionObj.
my expected output would :
[
{
"label": "Dashboard",
"value": "can_view"
},
{
"label": "token",
"value": "public",
"content": [{
"key": "test",
"value": "public"
}
{
"key": "token",
"value": "can_create"
}
]
},
{
"label": "OA deal",
"content": [
{
"label": "view oadeal",
"key": "OA deal",
"value": "can_view"
},
{
"label": "Deposit",
"key": "Deposit",
"value": "can_view"
}
]
},
{
"label": "Journals",
"content": [
{
"label": "Add Journal",
"key": "Journals",
"value": "can_create"
}
]
}
]

Sort array of array dynamically based on propery value in javascript

I have JSON which Each records attributes has seperate object, I want to sort by attributes dynamically.
I am manage to do dthe sorting with attributes, but it's static code. How Could I make it dynamically ?
Please find running code on : https://jsfiddle.net/b8fv4L1z/3/
var json = [
[
{
"apiName": "Name",
"value": "Bob"
},
{
"apiName": "CompanyName",
"value": "Google"
}
],
[
{
"apiName": "Name",
"value": "Micky"
},
{
"apiName": "CompanyName",
"value": "Amazon"
}
],
[
{
"apiName": "Name",
"value": "Donal"
},
{
"apiName": "CompanyName",
"value": "Facebook"
}
]
];
function Comparator(a, b, ) {
if (a[1].value < b[1].value) return -1; // a[1] sort by CompanyName If I put a[0] it will sort by Name.
if (a[1].value > b[1].value) return 1; // a[1] sort by CompanyName If I put a[0] it will sort by Name.
return 0;
}
json = json.sort(Comparator);
console.log(JSON.stringify(json));
Expected Result:
(Sorted by apiName = CompanyName):
[[{"apiName":"Name","value":"Micky"},{"apiName":"CompanyName","value":"Amazon"}],[{"apiName":"Name","value":"Donal"},{"apiName":"CompanyName","value":"Facebook"}],[{"apiName":"Name","value":"Bob"},{"apiName":"CompanyName","value":"Google"}]]
(Sorted by apiName = Name):
[[{"apiName":"Name","value":"Bob"},{"apiName":"CompanyName","value":"Google"}],[{"apiName":"Name","value":"Donal"},{"apiName":"CompanyName","value":"Facebook"}],[{"apiName":"Name","value":"Micky"},{"apiName":"CompanyName","value":"Amazon"}]]
With comparator made higher-order function.
var json = [
[{
"apiName": "Name",
"value": "Bob"
},
{
"apiName": "CompanyName",
"value": "Google"
}
],
[{
"apiName": "Name",
"value": "Micky"
},
{
"apiName": "CompanyName",
"value": "Amazon"
}
],
[{
"apiName": "Name",
"value": "Donal"
},
{
"apiName": "CompanyName",
"value": "Facebook"
}
]
];
function findValueByProperty(item, propertyName) {
var relevantRow = item.find(function(content) {
return content.apiName === propertyName;
});
return relevantRow.value;
}
function comparator(propertyName) {
return function(a, b) {
var valueA = findValueByProperty(a, propertyName);
var valueB = findValueByProperty(b, propertyName);
return valueA.localeCompare(valueB)
}
}
console.log('json==>' + JSON.stringify(json));
console.log(json.slice().sort(comparator("CompanyName")));
console.log(json.slice().sort(comparator("Name")));
You could find the wanted property. Maybe you need a default value, like an empty string.
function sort(array, apiName) {
const
getValue = array =>
(array.find(o => o.apiName === apiName) || { value: '' }).value;
return array.sort((a, b) => getValue(a).localeCompare(getValue(b)));
}
var array = [[{ apiName: "Name", value: "Bob" }, { apiName: "CompanyName", value: "Google" }], [{ apiName: "Name", value: "Micky" }, { apiName: "CompanyName", value: "Amazon" }], [{ apiName: "Name", value: "Donal" }, { apiName: "CompanyName", value: "Facebook" }]];
console.log(sort(array, 'CompanyName'));
console.log(sort(array, 'Name'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you have always the same data structure, you could get the apiName in advance and sort without a finding for every element.
function sort(array, apiName) {
const index = array[0].findIndex(o => o.apiName === apiName);
return array.sort((a, b) => a[index].value.localeCompare(b[index].value));
}
var array = [[{ apiName: "Name", value: "Bob" }, { apiName: "CompanyName", value: "Google" }], [{ apiName: "Name", value: "Micky" }, { apiName: "CompanyName", value: "Amazon" }], [{ apiName: "Name", value: "Donal" }, { apiName: "CompanyName", value: "Facebook" }]];
console.log(sort(array, 'CompanyName'));
console.log(sort(array, 'Name'));
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to assign values from one object to another in javascript

I am trying to assign values from the below "details" object to the each "model" in the "formFields" array.
Here is the details object
const details = {
criteriaName: "hello",
criteriaType: "type a",
description: "world"
}
Here is the formFields array
From the above details object I was trying to assign each value from each key to the each model from the formFields array
const formFields = [
{
"title": "Title 1",
"columns": 2,
"fields": {
"criteriaName": {
"type": "text",
"label": "Criteria Name",
"id": 'criteriaName',
"model": "",
"required": true,
"show": true,
"rules": [
v => !!v || 'Criteria Name is required',
]
},
"criteriaType": {
"type": "select",
"label": "Criteria type",
"id": "criteriaType",
"options": ['Dependent', 'Independent', 'Static'],
"model": "",
"required": true,
"rules": [
v => !!v || 'Criteria Type is required',
],
"show": true,
},
}
},
{
'title': "Title 2",
"columns": 1,
"fields": {
"description": {
"type": "description_notes",
"label": "Description",
"id": "description",
"required": false,
"model": '',
"show": true,
}
}
}
]
How can I achieve this?
Thank you.
formFields.forEach((form) => {
if (form.fields.criteriaName) form.fields.criteriaName.model = details.criteriaName;
if (form.fields.criteriaType) form.fields.criteriaType.model = details.criteriaType;
if (form.fields.description) form.fields.description.model = details.description;
})
This would make changes in original array.
You can read more about forEach here
if you want to create a new array with changes you can use:
let newFormFields = formFields.map((form) => {
if (form.fields.criteriaName) form.fields.criteriaName.model = details.criteriaName;
if (form.fields.criteriaType) form.fields.criteriaType.model = details.criteriaType;
if (form.fields.description) form.fields.description.model = details.description;
return form;
});
and you can read more about map here
welcome to Stack Overflow. The following code will probably do what you're asking for. It is a bit much to explain all the details but you can ask in the comments and read some of the documentation
const details = {
criteriaName: 'hello',
criteriaType: 'type a',
description: 'world',
};
const formFields = [
{
title: 'Title 1',
fields: {
criteriaName: {
id: 'criteriaName',
model: '',
},
criteriaType: {
id: 'criteriaType',
model: '',
},
},
},
{
title: 'Title 2',
fields: {
description: {
id: 'description',
model: '',
},
nonExisting: {
id: 'nonExisting',
model: 'this will not be replaced',
},
},
},
];
const result = formFields.map((formField) => ({
...formField,
fields: Object.entries(formField.fields).reduce(
(fields, [key, value]) =>
details[key]
? {
...fields,
[key]: { ...value, model: details[key] },
}
: fields,
formField.fields,
),
}));
console.log(result);

Categories