Create a JSON object using an array of nodes - node.js

I the results of a table that gives me the dynamic hierarchy of fields that i am needing to use to create a JSON object from.
EX:
[ [ 'request', 'cycleRange' ],
[ 'request', 'cycleRange', 'from' ],
[ 'request', 'cycleRange', 'to' ],
[ 'request', 'cycleRange', 'to', 'month' ],
[ 'request', 'cycleRange', 'to', 'year' ],
[ 'request', 'datestmt' ],
[ 'request', 'singleTran' ],
[ 'request', 'singleTran', 'datePost' ],
[ 'request', 'singleTran', 'dateTo' ] ]
The JSON object i am expecting to create with this sample should be:
let expected = {
request: {
cycleRange: {
from: null,
to: {
month: null,
year: null
}
},
datestmt: null,
singleTran: {
datePost: null,
dateTo: null
}
}
};
I am using this function to try and build the object, but i am only getting the final array as the object:
let final = {};
for (let i of heirarchy) {
assign(final, i, null);
}
function assign(obj, keyPath, value) {
let lastKeyIndex = keyPath.length - 1;
let key;
let i;
for (i=0; i<lastKeyIndex; i++) {
key = keyPath[i];
if (!(key in obj)) {
obj[key] = {};
} else {
if (i !== lastKeyIndex) {
obj[key] = {};
}
}
obj = obj[key];
}
obj[keyPath[lastKeyIndex]] = value;
}
Currently my results i am only getting: (the last index of my source hierarchy)
{"request":{"singleTran":{"dateTo":null}}}
Any help would be appreciated.

because obj is referencing final, this line is likely causing you mischief:
obj = obj[key];
This will overwrite the obj (in this case final) every iteration of hte loop. I would recommend keeping track of a separate variable so that final remains un-mutated

I have this solution but I think there is a lot of room for simplification and improvement, also I replaced null by {}.
You could use _.set from lodash: https://lodash.com/docs/4.17.11#set to replace set in my example.
const array = [ [ 'request', 'cycleRange' ],
[ 'request', 'cycleRange', 'from' ],
[ 'request', 'cycleRange', 'to' ],
[ 'request', 'cycleRange', 'to', 'month' ],
[ 'request', 'cycleRange', 'to', 'year' ],
[ 'request', 'datestmt' ],
[ 'request', 'singleTran' ],
[ 'request', 'singleTran', 'datePost' ],
[ 'request', 'singleTran', 'dateTo' ] ];
const set = (path, obj) => path.split('.').reduce((o,i)=> {
return o[i] ? o[i] : o[i] = {};
}, obj)
let myNewObject = {};
let path = '';
array.forEach(element => {
path = '';
element.forEach(key => {
path = path ? path + '.' + key : key;
set(path, myNewObject);
});
});
console.log(myNewObject);

I wound up using package deepmerge to merge the individual results into my final object
const deepmerge = require('deepmerge');
let final = {};
for (let i of heirarchy) {
let temp = {};
assign(temp, i, null);
final = deepmerge(final, temp);
}

Related

NodeJs How to convert parent-child json array to json-object with key-id and value array of all childes

I have a parent-child array that lookes like this:
const myTestArray = [
{
"id": "1",
"parent": "root"
},
{
"id": "2",
"parent": "1"
},
{
"id": "3",
"parent": "1"
},
{
"id": "4",
"parent": "2"
},
{
"id": "5",
"parent": "4"
}
];
From this I have been able to create an nested parent-child json-tree, but now I need another data-structure that looks like this:
const childesArray = {
"1": ["1", "2", "3", "4", "5"],
"2": ["2", "4", "5"],
"3": ["3"],
"4": ["4", "5"],
"5": ["5"]
};
For each element in myTestArray I need an array that contains all childes (deep) for that element.
I guess I also here can use reduce on this array, but I'm stucked right now how I can achive this. Anyone that can point me in the right direction? Or have any solution on this?
Br. Rune
There are several ways to achieve this, but I tried to keep it simple. Take a look at this example.
const nesting = {};
myTestArray.forEach((item) => {
const { parent, id } = item;
if (!nesting[id]) {
nesting[id] = [id];
}
if (!nesting[parent] && parent !== "root") {
nesting[parent] = [parent];
}
if (parent !== "root") {
nesting[parent].push(id);
}
});
console.log(nesting);
//
// {
// "1":["1","2","3"],
// "2":["2","4"],
// "3":["3"],
// "4":["4","5"],
// "5":["5"]
// }
//
You could take the JSON object and iterate through it with a ForEach loop. You can then append an element to the index of its parent, and you would end up with a 2-dimensional array such as the one you specified.
var newArr = {};
myTestArray.forEach(e => {
newArr[e.id] = [e.id];
if(e.parent != "root"){
newArr[e.parent].push(e.id);
}
});
would produce
newArr = { '1': [ '1', '2', '3' ],
'2': [ '2', '4' ],
'3': [ '3' ],
'4': [ '4', '5' ],
'5': [ '5' ] }
Have been using my day on this :)
Think I have a solution now.
Suggestions on improvements are welcome.
function getNestedChildrenArray(inputArray: Array<Record<string,unknown>>, inputId: string) {
let myA: Array<string> = inputArray.reduce((previousValue: Array<string>, currentValue: Record<string,unknown>) => {
if(typeof currentValue.id === 'string' && inputId === currentValue.parent) previousValue.push(currentValue.id);
return previousValue;
}, []);
if(myA.length > 0) {
myA = myA.concat(myA.reduce((previousValue: Array<string>, currentValue: string) => {
previousValue = previousValue.concat(getNestedChildrenArray(inputArray, currentValue));
return previousValue;
},[]));
}
return myA;
}
function getNestedChildrenArrayJsonObject(inputArray: Array<Record<string,unknown>>) {
return inputArray.reduce((previousValue: Record<string,unknown>, currentValue: Record<string,unknown>, index, array) => {
if(typeof currentValue.id === 'string') {
previousValue[currentValue.id] = [currentValue.id].concat(getNestedChildrenArray(array, currentValue.id));
}
return previousValue;
}, {});
}
console.log(getNestedChildrenArrayJsonObject(myTestArray));

Not getting expected JSON format when using xml2js, can it be fixed?

When I try to convert an XML file (requested from an external server) to JSON, it seems to me that xml2json does convert it however, not to a correct JSON file. Is there something that needs to be adjusted. I seem to be missing quotes for the keys.
This is my current code
app.get('/api/convertabstract/:id', async (req, res, next) => {
var data = '';
var finaldata = '';
function vertaaldata(){
return new Promise (resolve => {
https.get('https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=pubmed&id=' + ' 11748933,11700088' +'&retmode=xml', function(res) {
if (res.statusCode >= 200 && res.statusCode < 400) {
res.on('data', function(data_) { data += data_.toString(); });
res.on('end', function() {
parser.parseString(data, function(err, result) {
finaldata = util.inspect(result, false, null, true);
}), resolve('klaar');
});
}})})
}
async function calltranslator() {
const result = await vertaaldata();
console.log(finaldata.PubmedArticleSet);
res.send('dit is de data:' + finaldata)
}
calltranslator();
});
JSON output:
{ PubmedArticleSet: {
PubmedArticle: [
{
MedlineCitation: [
{
[32m'$'[39m: { Status: [32m'MEDLINE'[39m, Owner: [32m'NLM'[39m },
PMID: [ { _: [32m'11748933'[39m, [32m'$'[39m: { Version: [32m'1'[39m } } ],
DateCompleted: [ { Year: [ [32m'2002'[39m ], Month: [ [32m'03'[39m ], Day: [ [32m'04'[39m ] } ],
DateRevised: [ { Year: [ [32m'2006'[39m ], Month: [ [32m'11'[39m ], Day: [ [32m'15'[39m ] } ],
Article: [
{
[32m'$'[39m: { PubModel: [32m'Print'[39m },
Journal: [
{
ISSN: [ { _: [32m'0011-2240'[39m, [32m'$'[39m: { IssnType: [32m'Print'[39m } } ],
JournalIssue: [....etc....
As far as I can tell, xml2js and xml2json are completely unrelated libraries. Which are you actually using - you mention both! xml2js doesn't claim to generate JSON, it claims to generate Javascript.

Creating a Request Params for batchWriteItem(params,function(){}) in javaScript

I have been trying to create params for batchWriteItem(), when I create them like this it perfactly works.
var params = {
RequestItems: {
'tableNameXYZ' : [ //hardcoded
{
PutRequest: {
"Item" : {
"indexNumber":{
"N": xyz
},...
}
},
PutRequest: {
"Item" : {
"indexNumber":{
"N": xyz1
},...
}
}
}
]
};
But I am unable to work on it when I have table name in a variable:
var tableName = 'tableNameXYZ';
var params = {
RequestItems: {
tableName : [
{
PutRequest: {
"Item" : {
"indexNumber":{
"N": xyz
},...
}
},
PutRequest: {
"Item" : {
"indexNumber":{
"N": xyz1
},...
}
}
}
]
};
Obviously this wouldn't have worked since I played rough with JSON object here.
Then I tried this:
var params = {};
params.RequestItems[tableName] = [
{
PutRequest: {
"Item" : {
"indexNumber":{
"N": xyz
},...
}
},
PutRequest: {
"Item" : {
"indexNumber":{
"N": xyz1
},...
}
}
}
];
Now its throwing me an error like:
TypeError: Cannot set property 'tableNameXYZ' of undefined
at exports.handler (/var/task/index.js:181:32)
Can someone Pointout my mistake?
Thanks.
You've got this error because:
var params = {};
Creates an empty object (called params) with no properties, but:
params.RequestItems[tableName] = [ // ...rest of code
Tries to assign params.RequestItems.the-value-of-tableName.
But params has no property called RequestItems and so, params.RequestItems is undefined. And it is a TypeError to try and set the value of a property (in this case the-value-of-tableName) on undefined.
To fix this, use:
var params = {};
params.RequestItems = {}; // assign RequestItems an empty object.
params.RequestItems[tableName] = [ // ...rest of code.

How to collapse arrays of one string element into its string?

Assume
{
"foo":[
"baz"
],
"bar": {
"blarg": [
"blippo"
],
"blunder": {
"bus": [
{
"bigly": [
"bugnuts"
]
}
]
}
},
"blather": [
{
"bumpy": [
"bugaloo"
]
},
{
"blither": {
"bother": [
"bingo"
]
}
}
]
}
What would be the most efficient way (preferably using lodash) to convert such that all leaves that are arrays of one member now contain that member, and not the array? As in:
{
"foo": "baz",
"bar": {
"blarg": "blippo",
"blunder": {
"bus": {
"bigly": "bugnuts"
}
}
},
"blather": [
{
"bumpy": "bugaloo"
},
{
"blither": {
"bother": "bingo"
}
}
]
}
The object is much larger than the one I've presented here, and so has many possible paths.
I've tried first getting a list of paths, as in:
foo[0]
foo
bar.blarg[0]
bar.blarg
bar.blunder.bus[0]
bar.blunder.bus
bar.blunder
bar
blather[0].bumpy[0]
blather[0].bumpy
blather[0]
blather[1].blither.bother[0]
blather[1].blither.bother
blather[1].blither
blather[1]
blather
and attempting to do the mutation both depth and breadth first, but of course, the first mutation has the possibility of invalidating other paths.
I'm thinking it's a question of recursion, but the solution eludes me.
Here's a cloneDeepWith() approach that also covers collapsing arrays that contains a single object.
var result = _.cloneDeepWith(data, function customizer(value) {
if(_.isArray(value) && _.size(value) === 1) {
value = value[0];
return _.isObject(value)?
_.cloneDeepWith(value, customizer):
value;
}
});
console.log(result);
var data = {
"foo":[
"baz"
],
"bar": {
"blarg": [
"blippo"
],
"blunder": {
"bus": [
{
"bigly": [
"bugnuts"
]
}
]
}
},
"blather": [
{
"bumpy": [
"bugaloo"
]
},
{
"blither": {
"bother": [
"bingo"
]
}
}
]
};
var result = _.cloneDeepWith(data, function customizer(value) {
if(_.isArray(value) && _.size(value) === 1) {
return _.isObject(value[0])?
_.cloneDeepWith(value[0], customizer):
value[0];
}
});
console.log(result);
body > div { min-height: 100%; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
cloneDeepWith provides a way to customise an object as you clone it.
const {cloneDeepWith, isArray} = require('lodash')
const flatter = cloneDeepWith(data, value => {
if ( isArray(value) && value.length === 1 ) return value[0]
})
A recursive forEach would allow you to mutate the existing object (or forIn/forOwn if required).
const {forEach} = require('lodash')
function process(obj){
forEach(obj, (val, key)=> {
if ( isArray(val) && val.length === 1 ) return obj[key] = val[0]
if ( isObject(val) ) process(val)
})
}

Inline array store data in ExtJS4?

I'm unable to load inline array data into a store. In particular, this fails.
Can someone explain why? I even tried adding a memory proxy with an array reader and still no dice.
Ext.define('MyApp.store.ComboboxState', {
extend: 'Ext.data.Store',
constructor: function(cfg) {
var me = this;
cfg = cfg || {};
me.callParent([Ext.apply({
autoLoad: true,
storeId: 'ComboboxState',
data: [
[
'AL',
'Alabama'
]
]
,fields: [
{
name: 'state'
},
{
name: 'name'
}
]
}, cfg)]);
}
});
Still doesn't work with this memory proxy/array reader:
proxy: {
type: 'memory',
reader: {
type: 'array'
}
}
Just extend from ArrayStore, like this:
Ext.define('MyApp.store.ComboboxState', {
extend: 'Ext.data.ArrayStore',
constructor: function(cfg) {
var me = this;
cfg = cfg || {};
me.callParent([Ext.apply({
autoLoad: true,
storeId: 'ComboboxState',
data: [
[
'AL',
'Alabama'
]
]
,fields: ['state', 'name' ]
}, cfg)]);
}
});
JsFiddle to try: http://jsfiddle.net/voidmain/hKwbJ/

Resources