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.
Related
I'm not sure what I'm doing wrong but I would like to map an array of objects. This array of objects it's dynamic, this means sometimes it has 4 objects and sometimes 20, so I need to make sure it will work no matters how many entries it has:
var obj = [
{
"key": "one",
"value": "something"
},
{
"key": "two",
"value": "random"
},
{
"key": "blue",
"value": "chicken"
},
{
"key": "orange",
"value": "sportsman"
}
];
I need to pass this objects into one to send it through a POST call that should match this format:
form: {
'one': 'something',
'two': 'random',
'blue': 'chicken',
'orange': 'sportsman'
}
I can access to item.value but IDK why I can't item.key:
var obj = [
{
"key": "one",
"value": "something"
},
{
"key": "two",
"value": "random"
},
{
"key": "blue",
"value": "chicken"
},
{
"key": "orange",
"value": "sportsman"
}
];
var combined = obj.map(function combined(item) {
return {
item.key: item.value
};
})
console.log(combined);
Any ideas? Thanks.
You shouldn't return an object from your callback function. Return an array of the key and value, then use Object.fromEntries() to turn this into a single object with those keys and values.
var obj = [{
"key": "one",
"value": "something"
},
{
"key": "two",
"value": "random"
},
{
"key": "blue",
"value": "chicken"
},
{
"key": "orange",
"value": "sportsman"
}
];
var combined = Object.fromEntries(obj.map(item => [item.key, item.value]));
console.log(combined);
1) You can't use a map here because the map will return a new array. You can achieve this result using reduce
var obj = [
{
key: "one",
value: "something",
},
{
key: "two",
value: "random",
},
{
key: "blue",
value: "chicken",
},
{
key: "orange",
value: "sportsman",
},
];
const result = obj.reduce((acc, curr) => {
const { key, value } = curr;
acc[key] = value;
return acc;
}, {});
console.log(result);
2) You can also do this using for..of loop
var obj = [{
key: "one",
value: "something",
},
{
key: "two",
value: "random",
},
{
key: "blue",
value: "chicken",
},
{
key: "orange",
value: "sportsman",
},
];
const result = {};
for (let o of obj) {
result[o.key] = o.value;
}
console.log(result);
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>);
I'm currently converting my flat data to a hierarchy with 2 levels, but I now need to add an additional 3rd level to this data. I haven't been able to figure out how to modify by existing method so far. I'm open to completely new methods also.
Here is the code I'm currently using to do the conversion:
chartdata = sfdata.data.reduce((acc, {
items: [cat, val, salesTY, salesLY, unitsTY, unitsLY]
}) => {
acc[cat] = acc[cat] || [];
acc[cat].push({
name: val,
salesTY: salesTY,
salesLY: salesLY,
unitsTY: unitsTY,
unitsLY: unitsLY
});
return acc;
}, {});
// Generate desired output structure.
chartdata = Object.entries(chartdata).map(([k, v]) => ({
category: k,
children: v
}));
It maps to the different categories and then converts that map to a category>children structure
And here is the fiddle where I'm using this tree structure: http://jsfiddle.net/zt4nhxcw/3/
I've started a new fiddle here with the new Brand data included: http://jsfiddle.net/t1uz85b2/
The goal is to add a 3rd level. So every child at the 2nd level would have Brand underneath of them.
Here is a very small snippet of how the data comes in:
[
{
"items": [
"SSD",
"PBNA",
"MOUNTAIN DEW",
851255.3500000001,
672407.8399999997,
782364.9999999991,
641579.0000000006
],
"hints": {
"index": 0
}
},
{
"items": [
"Energy",
"RED BULL NORTH AMERICA",
"RED BULL",
836632.2299999997,
654021.2899999995,
267216,
214321.00000000015
],
"hints": {
"index": 1
}
},
{
"items": [
"SSD",
"PBNA",
"PEPSI",
478704.02999999974,
392746.69999999995,
533557.0000000006,
457008.0000000001
],
"hints": {
"index": 4
}
},
{
"items": [
"Energy",
"RED BULL NORTH AMERICA",
"RED BULL EDITIONS",
449618.55000000016,
328150.8999999997,
162428.9999999999,
117521.00000000001
],
"hints": {
"index": 5
}
},
{
"items": [
"SSD",
"CCNA",
"COKE",
349685.7899999996,
276766.95,
445485.0000000002,
351214.0000000003
],
"hints": {
"index": 9
}
}
]
And here is the final structure I'm trying to achieve:
[
{
"category": "SSD",
"children": [
{
"brand": "PBNA",
"children": [
{
"name": "MOUNTAIN DEW",
"salesTY": 851255.3500000001,
"salesLY": 672407.8399999997,
"unitsTY": 782364.9999999991,
"unitsLY": 641579.0000000006
}
]
},
{
"brand": "CCNA",
"children": [
{
"name": "COKE",
"salesTY": 349685.7899999996,
"salesLY": 276766.95,
"unitsTY": 445485.0000000002,
"unitsLY": 351214.0000000003
}
]
}
]
},
{
"category": "Energy",
"children": [
{
"brand": "RED BULL NORTH AMERICA",
"children": [
{
"name": "RED BULL",
"salesTY": 836632.2299999997,
"salesLY": 654021.2899999995,
"unitsTY": 267216,
"unitsLY": 214321.00000000015
},
{
"name": "RED BULL EDITIONS",
"salesTY": 449618.55000000016,
"salesLY": 328150.8999999997,
"unitsTY": 162428.9999999999,
"unitsLY": 117521.00000000001
}
]
}
]
}
]
You could take a dynamic approach and take an array with the keys for all values and a limit of the wanted depth.
const
data = [{ items: ["SSD", "PBNA", "MOUNTAIN DEW", 851255.3500000001, 672407.8399999997, 782364.9999999991, 641579.0000000006], hints: { index: 0 } }, { items: ["Energy", "RED BULL NORTH AMERICA", "RED BULL", 836632.2299999997, 654021.2899999995, 267216, 214321.00000000015], hints: { index: 1 } }, { items: ["SSD", "PBNA", "PEPSI", 478704.02999999974, 392746.69999999995, 533557.0000000006, 457008.0000000001], hints: { index: 4 } }, { items: ["Energy", "RED BULL NORTH AMERICA", "RED BULL EDITIONS", 449618.55000000016, 328150.8999999997, 162428.9999999999, 117521.00000000001], hints: { index: 5 } }, { items: ["SSD", "CCNA", "COKE", 349685.7899999996, 276766.95, 445485.0000000002, 351214.0000000003], hints: { index: 9 } }],
keys = ['category', 'brand', 'name', 'salesTY', 'salesLY', 'unitsTY', 'unitsLY'],
limit = 2,
result = data
.reduce((temp, { items }) => {
keys
.slice(0, limit)
.reduce(function (r, k, i) {
if (!r[items[i]]) {
r[items[i]] = { _: [] };
r._.push({ [k]: items[i], children: r[items[i]]._ });
}
return r[items[i]];
}, temp)
._
.push(keys
.slice(limit)
.reduce((o, k, i) => (o[k] = items[i + limit], o), {})
);
return temp;
}, { _: [] })
._;
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
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
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; }