Having trouble iterating through object in Angular - javascript

I have an app in Angular 10 I am writing. I am passing in a JSON string. I am trying to convert that to an object. I am getting a compiler error saying
'inputObj' is not iterable.
The code is this:
interface FileNode {
name: string;
data?: FileNode[];
}
....
....
ParseData(input: string){
let output: FileNode[] = [];
let children: FileNode[] = [];
let inputObj: any[] = JSON.parse(input);
for (let node of inputObj) {
console.log(JSON.stringify(node));
}
// this is what I need in the end...
let newNode: FileNode = {
name: 'Name',
data: children,
}
output.push(newNode);
return output;
}
The data I am passing in is this:
{
"Data":"/",
"Nodes":[
{
"Data":"New Text Document.txt",
"Nodes":[]
},
{
"Data":"L2",
"Nodes":[
{
"Data":"README.txt",
"Nodes":[]
},
{
"Data":"L2A",
"Nodes":[
{
"Data":"README_L2A.txt",
"Nodes":[]
}
]
}
]
},
{
"Data":"L3",
"Nodes":[
{
"Data":"README_L3.txt",
"Nodes":[]
}
]
}
]
}
What am I doing wrong. I am sure this is something stupid but I just cannot see it.

Assuming you would like to loop through Nodes array you would need to get it first like
let inputObj:any = JSON.parse(input);
const nodes:any[] = inputObj['Nodes'];
then iterate.
also your structure seems deeply nested, you could make some recursive calls like
logNodes(node:any){
if (!node || node.length === 0)
return;
console.log(node);
for(n of node['Nodes'])
this.logNodes(node.Nodes[i]);
}
ngOnInit(){
let inputObj:any = JSON.parse(input);
const nodes:any[] = inputObj['Nodes'];
this.logNodes(nodes)
}
(not tested)

Related

track path of recursive function

I'm trying to track path of a deep nested value in json object but having hard time getting the path. Each Item is an array of objects and can have child items. If the object c exists in the json data it is always located in the last item array.
item: [
{
a:5,
item: [
{
item: [
{c:1},
{x:4},
],
...
},
{},
{}
]
},
{},
{}
]
const findPath = (items) => {
let path = []
items.forEach((item,i) => {
if('item' in item){
path = path.concat(findPath(item.item))
}
else if('c' in item) {
path.push(i)
}
})
return path
}
if I have 3 c objects with different item depths, then I would have:
[
[0,0,0], //item[0].item[0].item[0].c
[1,0], //item[1].item[0].c
[4]] , //item[4].c
Any help?
Your main problem here is that you don't track the common case. You store the index only when you found a leaf, but you want all the steps in between. This being recursion, you also have to carry your return values with you, or you end up stepping on them. This works:
objects = [
{},
{
item: [
{},
{},
{
a:5,
item: [
{
item: [
{c:1},
{x:4},
]
},
{},
{}
]
},
{}
]
}
]
const findPath = (items, current_path, matching_paths) => {
items.forEach((item,i) => {
if('item' in item){
current_path.push(i);
current_path = current_path.concat(
findPath(item.item, current_path, matching_paths)
);
}
else if('c' in item) {
current_path.push(i);
matching_paths.push( current_path.slice() );
current_path = [];
}
})
}
var path = [];
var paths = [];
findPath(objects, path, paths);
console.log(paths); //[[1, 2, 0, 0]]
If C is found push a path object to the path array and update that path object for the rest of the paths.
const findPath = (items) => {
let path = []
items.forEach((item,i) => {
if('item' in item){
let item_path = findPath(item.item)
if(item_path.length > 0){
item_path[0].path.push(i)
path.push(item_path[0])
}
}
else if('c' in item){
path.push({path:[i], c:item.c})
}
})
return path
}
The function must be recursive, which means it should call itself with different parameters and not loop forever.
Below is what you are looking for. I made it in TypeScript to make sure I typed it correctly, but just take off all type definitions and it becomes JavaScript:
const trackPath: number[][] = [];
function findPath(topItem: any, path: number[], position: number): void
{
const currentPath = path.slice();
currentPath.push(position);
const newTopItem = topItem['item'];
if (Array.isArray(newTopItem)) {
// here is the recursion for each subitem
newTopItem.forEach((item, i) => findPath(item, currentPath, i));
}
if ('c' in topItem) {
trackPath.push(currentPath);
}
}
// this is the main method to call
function actuallyGetThePath(myTopItem: any): number[][] {
findPath(myTopItem, [], 0);
return trackPath;
}
Good luck!

How to fix my recursive function? I am receiving either an array of an array of data

I am trying to create a recursive function that will go through an object similar to a directory with subdirectories, and output the'file' objects in an array. However, it seems that i am getting an array of arrays rather than a simple array with the objects I am expecting to see...
The bottom of the code has some console.logs that return:
console.log(findEntry(repAll, '/first')); // ===> [ { name: '/first' }, [] ]
console.log(findEntry(repAll, '/second')); // ===> [ [ { name: '/second' }, { name: '/second' } ] ]
const repAll = {
file1: {
name: "/first"
},
SubDir: {
file2: {
name: "/second"
},
file3: {
name: "/second"
}
}
};
const req = {};
function findEntry(data, name) {
let x = [];
for (const value of Object.values(data)) {
// Is this a leaf node or a container?
if (value.name) {
// Leaf, return it if it's a match
if (value.name === name) {
x.push(value);
}
} else {
// Container, look inside it recursively
const entry = findEntry(value, name);
x.push(entry);
}
}
return x;
}
console.log('search: /first');
console.log(findEntry(repAll, '/first'));
console.log('search: /second');
console.log(findEntry(repAll, '/second'));
You could spread the result of findEntry instead of simply pushing the array.
const repAll = {
file1: {
name: "/first"
},
SubDir: {
file2: {
name: "/second"
},
file3: {
name: "/second"
}
}
};
const req = {};
function findEntry(data, name) {
let x = [];
for (const value of Object.values(data)) {
// Is this a leaf node or a container?
if (value.name) {
// Leaf, return it if it's a match
if (value.name === name) {
x.push(value);
}
} else {
// Container, look inside it recursively
x.push(...findEntry(value, name));
}
}
return x;
}
console.log('search: /first');
console.log(findEntry(repAll, '/first'));
console.log('search: /second');
console.log(findEntry(repAll, '/second'));
With your approach :
function findEntry(data, name,x) {
for (const value of Object.values(data)) {
// Is this a leaf node or a container?
if (value.name) {
// Leaf, return it if it's a match
if (value.name === name) {
x.push(value);
}
} else {
// Container, look inside it recursively
const entry = findEntry(value, name,x);
x.push(entry);
}
}
return x;
}
Now call it like this :
let arr=[];
console.log(findEntry(repAll, '/first',arr));

How to parse JSON having nested arrays in javascript or jquery

I want to parse JSON like below
{
"nodeId":3892718504,
"root":true,
"subs":[
{
"nodeId":3892717286
},
{
"nodeId":3892716092,
"subs":[
{
"nodeId":3892715856,
"subs":[
{
"nodeId":3892718592,
"subs":[
{
"nodeId":3892717580
}
]
}
]
}
]
},
{
"nodeId":3892717497
}
]
}
Each node can have subs and those subs can have nodes that can have their own subs. all I want is an array having all nodeId, how can I parse this JSON such that an array called nodes_list is populated with all nodeId.
I can use javascript or jquery.
I'm trying the following approach to get an array of nodeId
jQuery.each(response.topology, function(i,obj) {
if(i == "nodeId") {
node_list.push(obj)
}
if(i == "subs"){
jQuery.each(i, function(key,value) {
if(i == "nodeId") {
node_list.push(obj)
}
}
}
});
I just need a little hint on how it can be in an iterative manner.
This can be done with function generators.
Perhaps not the most enjoyable approach, but I'm pretty sure the other solutions will already imply using other ways, so here is a solution using generators.
PS: Beware of browser support: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
const input = {
"nodeId":3892718504,
"root":true,
"subs":[
{
"nodeId":3892717286
},
{
"nodeId":3892716092,
"subs":[
{
"nodeId":3892715856,
"subs":[
{
"nodeId":3892718592,
"subs":[
{
"nodeId":3892717580
}
]
}
]
}
]
},
{
"nodeId":3892717497
}
]
};
function* nodeLookup(obj) {
if (obj.nodeId) yield obj.nodeId;
if (obj.subs) for (var i = 0; i < obj.subs.length; i++) yield *nodeLookup(obj.subs[i]);
};
const node_ids = [...nodeLookup(input)];
console.log(node_ids);
Just use recursion to iterate over subs
var nodeIds = [];
if (data.nodeId) nodeIds.push(data.nodeId);
function fetchNodeIds (subs) {
if (!subs.length) return cb([]);
var abc = [];
subs.forEach(function (sub) {
abc.push(sub.nodeId);
if (sub.subs && sub.subs.length) abc = abc.concat(fetchNodeIds(sub.subs))
});
return abc;
}
nodeIds = nodeIds.concat(fetchNodeIds(data.subs));
console.log('--All nodeIds--', nodeIds)
It's straightforward to do recursively:
const gatherIds = ({nodeId, subs}, results = []) => subs
? [...results, nodeId, ...(subs .flatMap (sub => gatherIds (sub, results) ))]
: [...results, nodeId]
const response = {"nodeId": 3892718504, "root": true, "subs": [{"nodeId": 3892717286}, {"nodeId": 3892716092, "subs": [{"nodeId": 3892715856, "subs": [{"nodeId": 3892718592, "subs": [{"nodeId": 3892717580}]}]}]}, {"nodeId": 3892717497}]}
console .log (
gatherIds (response)
)
If your target environments don't support flatmap, it's easy enough to shim.

Convert JSON object to object which not in JSON format

I have a JSON object with the structure as below
const inputObj = {
"prop1": "val1",
"prop2": {
"prop2_1": "val2_1",
"prop2_2": "val2_2"
}
"prop3": "val3"
}
My objective: I would like to take the property, including the nested property, and store the result in a txt file, but not in JSON format. To make it clear, here is my expected output in the txt file:
{
prop1: {
id: 'prop1'
},
prop2_prop2_1: {
id: 'prop2.prop2_1'
},
prop2_prop2_2: {
id: 'prop2.prop2_2'
}
prop3: {
id: 'prop3'
}
}
So far, I could write the non nested property, but still not in the structure which I expected. Here is the result so far:
{
"prop1": "prop1",
"prop3": "prop3"
}
Its still in JSON format, not in the structure that I expected, and the nested property still not caught (I still thinking how to get it)
here is the code so far to make my current result:
const fs = require('fs')
const fileName = "./results.txt"
function getAllKeys(obj, path = [], result = []) {
Object.entries(obj).forEach(([k, v]) => {
if (typeof v === 'object') getAllKeys(v, path.concat(k), result)
else result.push(path.concat(k).join("."))
})
return result
}
const inputToFile = getAllKeys(inputObj)
// console.log(inputToFile)
// result of the console.log
// prop1
// prop2.prop2_1
// prop2.prop2_2
// prop3
const newObj = {}
for (var i = 0; i < inputToFile.length; i++) {
var input = inputToFile[i]
var dotIndex = input.indexOf('.') // to check if its from the nested JSON property of the inputObj
if (dotIndex === -1) {
// no dot or nested property in the JSON
newObj[input] = input.toString()
} else {
// if the input contain dot, which is a nested JSON
}
}
fs.writeFileSync(fileName, JSON.stringfy(newObj))
// if I use above line, the result in the file is as I had mention above. But, if the code is like below:
const finals = JSON.stringfy(newObj)
fs.writeFileSync(fileName, JSON.parse(finals))
// the output in the file is only "[Object object]" without double quote
Update
The reason why I need the result to be formatted like that, is because I want to use react-intl. I already have the locale file (the translation), which looks like the inputObj (the structure). Then, I need to make a file, which like this (below), so the lib could translate it:
import { defineMessages } from 'react-intl';
const MessagesId = defineMessages({
prop1: {
id: 'prop1'
},
prop2_prop2_1: {
id: 'prop2.prop2_1'
},
prop2_prop2_2: {
id: 'prop2.prop2_2'
},
prop3: {
id: 'prop3'
}
})
export default MessagesId;
Thats why, I need it to be not like JSON. Because I already have thousand codes for the translation, but need to define it in the MessagesId. It would be so much takes time rite if I do it manually .__.
Ps: the react-intl is works, the problem is only the converting as my initial questions
This script can handle multiple levels of nestied object.
const outputObj = {};
const convertNestedObj = (obj, parentKey = []) => {
for (key in obj) {
newParentKey = [...parentKey, key];
if (typeof obj[key] === 'object') {
convertNestedObj(obj[key], newParentKey);
} else {
outputObj[newParentKey.join('_')] = { id: newParentKey.join('_') };
}
}
};
convertNestedObj(inputObj);

Cannot read property 'concat' of undefined

to begin with, I have a multilevel of entities as in
country unit ----> customer reporting group ----> customers
each country unit has different customer reporting groups and each of the later has different customers
in the code the variable names are
cu ----> crg ---> customer
this is represented in a multilevel object called menuData:
menuData = {
cu1: {
CRG3: {
Customer1: {},
Customer5: {}
},
CRG7: {
Customer3: {},
Customer2: {},
Customer7: {}
}
},
cu4: {
CRG1: {
Customer2: {},
Customer4: {}
},
CRG3: {
Customer4: {}
}
}
};
what I wanted to do is to construct unique id for each level in a multilevel objects as well as in for example the ids for the customer units will be the same
cu1 and cu2 and so on
for the customer reporting groups the ids will consist of the cu + the crg as in
cu1+crg4
for the customer:
cu1+crg4+customer6;
what I did is a function called getIds
var getIds = function(menuData) {
var ids = {};
for (cu in menuData) {
ids[cu] = cu;
for (crg in menuData[cu]) {
if (!(ids[cu] in ids)) {
ids[cu] = {};
ids[cu][crg] = ids[cu].concat(crg);
} else ids[cu][crg] = ids[cu].concat(crg);
for (customer in menuData[cu][crg]) {
if (!ids[cu][crg]) {
ids[cu][crg] = {};
ids[cu][crg][customer] = ids[cu][crg].concat(customer);
} else ids[cu][crg][customer] = ids[cu][crg].concat(customer);
}
}
}
console.log(ids);
return ids;
};
the error I got is
Cannot read property 'concat' of undefined
what I have tried is that, because it says that it's undefined, I try to define it if its not already defined as in
if (!(ids[cu] in ids)) {
ids[cu] = {};
ids[cu][crg] = ids[cu].concat(crg);
}
if its not defined, define it and insert the value, but if its defined, only assign the value
else ids[cu][crg] = ids[cu].concat (crg );
why do I get this error? and how to get the the ids in multilevel objects ?
edit, excpected output is
ids = {
"cu1": {
"cu1+CRG3": { "cu1+CRG3+Customer1":{}, "cu1+CRG3+Customer5":{} },
"cu1+CRG7": { "cu1+CRG7+Customer3":{}, "cu1+CRG7+Customer2":{}, "cu1+CRG7+Customer7":{} }
},
"cu4": {
"cu4+CRG1": { "cu4+CRG1+Customer2":{}, "cu4+CRG1+Customer4":{} },
"cu4+CRG3": { "cu4+CRG3+Customer4":{}}
}
}
The Problem with your Code is that you are using Objects to store your data and Objects don´t have the Method "concat" only Arrays have the "concat" Method. Your Object must look like these to work:
menuData = [
"cu1": [
"CRG3": [ "Customer1":{}, "Customer5":{} ],
"CRG7": [ "Customer3":{}, "Customer2":{}, "Customer7":{} ]
],
"cu4": [
"CRG1": [ "Customer2":{}, "Customer4":{} ],
"CRG3": [ "Customer4":{}]
]
]
Here´s a reference : MDN Array.concat()
What can be confusing in JS is that an Object Property can be accessed like an Array.
Update after Expected Output was added:
okay than i think concat is not the right solution for your Problem.
Try it with something like this:
var ids = {};
var menuData = {
cu1: {
CRG3: {
Customer1: {},
Customer5: {}
},
CRG7: {
Customer3: {},
Customer2: {},
Customer7: {}
}
},
cu4: {
CRG1: {
Customer2: {},
Customer4: {}
},
CRG3: {
Customer4: {}
}
}
};
for (propKeyLevel1 in menuData){
ids[propKeyLevel1] = {};
var propLevel1 = ids[propKeyLevel1];
for(propKeyLevel2 in menuData[propKeyLevel1]){
propLevel1[propKeyLevel1+"+"+propKeyLevel2] = {};
var propLevel2 = propLevel1[propKeyLevel1+"+"+propKeyLevel2];
for(propKeyLevel3 in menuData[propKeyLevel1][propKeyLevel2]){
propLevel2[propKeyLevel1+"+"+propKeyLevel2+"+"+propKeyLevel3] = {};
}
}
}
console.log(ids);
concat is a method for for a String or an Array, here you call it on an object hence the error.
What you're trying to do is a bit unclear to me, but maybe you could try that :
ids[cu][crg] = crg;
instead of :
ids[cu][crg] = ids[cu].concat (crg );
Because that's what you seem to be trying.
I’d try it this way:
function getIds(dataIn, idsIn) {
idsIn = idsIn || [];
var dataOut = {}, idOut;
for (var idIn in dataIn) {
idsOut = idsIn.concat([idIn]);
dataOut[idsOut.join('+')] = getIds(dataIn[idIn], idsOut);
}
return dataOut;
}
Perfect use case for a recursive function passing down an array (idsOut) of the ids of the previous layers to generate the intended object keys. Pretty straight forward.

Categories

Resources