traversing through JSON string to inner levels using recursive function - javascript

I have a JSON input which can go to any number of levels.
I'm giving an input sample of
var d=getEntities( {"Categories":
{
"Facets":
[
{
"count": 1,
"entity": "Company",
"Company":
[
{
"entity": "Ford Motor Co",
"Ford_Motor_Co":
[
{
"count": 1,
"entity": "Ford"
}
]
}
]
},
{
"count": 4,
"entity": "Country",
"Country": [
{
"entity": "Germany",
"Germany": [
{
"count": 1,
"entity": "Germany"
}
],
"currency": "Euro (EUR)"
},
{
"entity": "Italy",
"Italy": [
{
"count": 1,
"entity": "Italy"
}
],
"currency": "Euro (EUR)"
},
{
"entity": "Japan",
"Japan": [
{
"count": 1,
"entity": "Japan"
}
],
"currency": "Yen (JPY)"
},
{
"entity": "South Korea",
"South_Korea": [
{
"count": 1,
"entity": "South Korea"
}
],
"currency": "Won (KRW)"
}
]
},
{"count": 5,
"entity": "Persons",
"Persons": [
{
"count": 2,
"entity": "Dodge"
},
{
"count": 1,
"entity": "Dodge Avenger"
},
{
"count": 1,
"entity": "Major League"
},
{
"count": 1,
"entity": "Sterling Heights"
}
]
}
]
}});
I want to add the key value "Entity" in all levels to an array using recursion,
I'm able to collect the data from first level using the string
<html>
<head>
<script src="jquery.js" type="text/javascript"></script>
<script type="text/javascript" src="dataDumper.js"></script>
<script type="text/javascript">
var testJSON = {"Categories":
{
"Facets":
[
{
"count": 1,
"entity": "Company",
"Company":
[
{
"entity": "Ford Motor Co",
"Ford_Motor_Co":
[
{
"count": 1,
"entity": "Ford"
}
]
}
]
},
{
"count": 4,
"entity": "Country",
"Country": [
{
"entity": "Germany",
"Germany": [
{
"count": 1,
"entity": "Germany"
}
],
"currency": "Euro (EUR)"
},
{
"entity": "Italy",
"Italy": [
{
"count": 1,
"entity": "Italy"
}
],
"currency": "Euro (EUR)"
},
{
"entity": "Japan",
"Japan": [
{
"count": 1,
"entity": "Japan"
}
],
"currency": "Yen (JPY)"
},
{
"entity": "South Korea",
"South_Korea": [
{
"count": 1,
"entity": "South Korea"
}
],
"currency": "Won (KRW)"
}
]
},
{"count": 5,
"entity": "Persons",
"Persons": [
{
"count": 2,
"entity": "Dodge"
},
{
"count": 1,
"entity": "Dodge Avenger"
},
{
"count": 1,
"entity": "Major League"
},
{
"count": 1,
"entity": "Sterling Heights"
}
]
}
]
}};
function scan(obj)
{
var k;
if (obj.hasOwnProperty('entity')) {
for (k in obj){
if (obj.hasOwnProperty(k)){
scan( obj[k] );
}
}
}
else{
if(k=='entity')
{
alert(obj.entity);
}
}
};
scan(testJSON);
</script>
</head>
<body>
</body>
</html>
How do I get in to the inner levels for JSON string using recursive functions?

I have made a jsfiddle which traverses every object,array and value in the JS object like so...
function scan(obj) {
var k;
if (obj instanceof Object) {
for (k in obj){
if (obj.hasOwnProperty(k)){
//recursive call to scan property
scan( obj[k] );
}
}
} else {
//obj is not an instance of Object so obj here is a value
};
};
I get no recursion error (in Chrome). Can you use this to do what you want?
If you need to test if an object is an array use if (obj instanceof Array)
To test if an object has an "entity" property use if (obj.hasOwnProperty('entity'))
To add (or modify an existing) "entity" property use obj.entity = value or obj['entity'] = value

(function recur( obj ) {
Object.keys( obj ).forEach( function( prop ) {
// Check if the property is an object
if ( ({}).toString.apply( prop ) === '[object Object]' ) {
// If it is, recall this function
recur( prop );
}
} );
} () );
I haven't added your logic, but you get the idea of how to recursively traverse your object.

Say I have a structure like the following:
var aObject = {
items: [],
children: {}
}
Children is an associative array that contains more aObjects. So it could look like this:
var aObject = {
items: [],
children: {
"subgroup1": {
items: [],
children: {}
},
"subgroup2": {
items: [],
children: {}
}
}
}
I have an item that contains an array of subgroups:
["subgroup1", "subgroup1a"]
Each subgroup is a 'location'. The item needs to be placed at:
aObject.children[array[0]].children[array[1]].items
At each level, we have to check if children[array[i]] exists, and if not, create it. You can't simply write aObject.children[array[0]].children[array[1]].items.push(item) because children[array[0]] might not already exist and we will get an error.
This can be solved using recursion! (AngularJS)
function recursive(aLevel, aItem, aArray, aIndex){
var lLevel = aLevel;
// If we have reached the end of the array
if (aIndex === aArray.length){
// Insert
aLevel.items.push(aItem);
} else {
// If the subgroup doesn't exist, create it
if (typeof aLevel.children[aArray[aIndex]] === 'undefined'){
aLevel.children[aArray[aIndex]] = {
items: [],
children: {}
};
}
// Move into
recursive(aLevel.children[aArray[aIndex]], aItem, aArray, aIndex+1);
}
}
aObject = {
items: [],
children: {},
}
angular.forEach(items, function(item, i){
var location = item.location;
if (location.length == 0){
aObject.items.push(item);
} else {
recursive(aObject, item, location, 0);
}
});
The final aObject would look like this:
var aObject = {
items: [],
children: {
"subgroup1": {
items: [],
children: {
"subgroup1a": {
items: [item],
children: {}
}
}
},
"subgroup2": {
items: [],
children: {}
}
}
}

Here is a function that i use often. It's easily modifiable to do many recursive tasks. For example if you add a bail flag you can quickly get the stack or add a callback function that makes it even more general. Anyway that's my 2 cents
var recursiveObjMap = (function(){
var stack = [];
var result = [];
// var bail = false;
return function map(data, key){
if (!$.isArray(data) && !$.isPlainObject(data) ) {
result.push(data);
return false
}
$.each(data, function(i, v){
if (key) stack.push(key);
map(v, i);
stack.pop();
});
return result;
};
})();
recursiveObjMap({a:'b',c:{d:{e:"f"}}}) // ['b', 'f']

const obj = [
{
count: 1,
entity: "Company",
Company: [
{
entity: "Ford Motor Co",
Ford_Motor_Co: [
{
count: 1,
entity: "Ford",
},
],
},
],
},
{
count: 4,
entity: "Country",
Country: [
{
entity: "Germany",
Germany: [
{
count: 1,
entity: "Germany",
},
],
currency: "Euro (EUR)",
},
{
entity: "Italy",
Italy: [
{
count: 1,
entity: "Italy",
},
],
currency: "Euro (EUR)",
},
{
entity: "Japan",
Japan: [
{
count: 1,
entity: "Japan",
},
],
currency: "Yen (JPY)",
},
{
entity: "South Korea",
South_Korea: [
{
count: 1,
entity: "South Korea",
},
],
currency: "Won (KRW)",
},
],
},
{
count: 5,
entity: "Persons",
Persons: [
{
count: 2,
entity: "Dodge",
},
{
count: 1,
entity: "Dodge Avenger",
},
{
count: 1,
entity: "Major League",
},
{
count: 1,
entity: "Sterling Heights",
},
],
},
];
function test(runObj) {
for (let i in runObj) {
typeof runObj[i] == "object" ? test(runObj[i]) : console.log(runObj[i]);
}
}
test(obj);

Related

How to filter from an object by iterating over it in js

I am trying to get the value of "type" from the object by iterating over it. The object looks like this.
{
"team": {
"table": [
{
"cityCode": 123,
"list": {
"players": [
{
"name": "peter",
"school": "x",
"awards": {
"type": "gold"
},
"year": 2019
}
]
}
},
{
"cityCode": 456,
"list": {
"players": [
{
"name": "Dave",
"school": "y",
"awards": {
"type": "silver"
},
"year": 2018
}
]
}
}
]
}
}
I am able to get the type values using this:
const table = team.table;
for (let i = 0; i < table.length; i++) {
const values = {
type: table[i].list.players
.filter((a) => a.awards != null)
.map((a) => a.awards.type)
.join(" "),
};
}
However, I want to use another filter on the "list" to filter non null lists. So how can I achieve that.
You want to check Check if 'list' key exists inside a team.table JSON object
you can check by
if(table[i].hasOwnProperty('list')){
}
code is
const table = team.table;
for (let i = 0; i < table.length; i++) {
if(table[i].hasOwnProperty('list')){
const values = {
type: table[i].list.players
.filter((a) => a.awards != null)
.map((a) => a.awards.type)
.join(" "),
};
}
}
1) You can get all type using flatMap and map as:
obj.team.table.flatMap((o) => o.list.players.map((o) => o.awards.type))
const obj = {
team: {
table: [
{
cityCode: 123,
list: {
players: [
{
name: "peter",
school: "x",
awards: {
type: "gold",
},
year: 2019,
},
],
},
},
{
cityCode: 456,
list: {
players: [
{
name: "Dave",
school: "y",
awards: {
type: "silver",
},
year: 2018,
},
],
},
},
],
},
};
const types = obj.team.table.flatMap((o) => o.list.players.map((o) => o.awards.type));
console.log(types);
2) Using forEach and destructuring as:
const obj = {
team: {
table: [
{
cityCode: 123,
list: {
players: [
{
name: "peter",
school: "x",
awards: {
type: "gold",
},
year: 2019,
},
],
},
},
{
cityCode: 456,
list: {
players: [
{
name: "Dave",
school: "y",
awards: {
type: "silver",
},
year: 2018,
},
],
},
},
],
},
};
const table = obj.team.table;
const types = [];
for (let i = 0; i < table.length; i++) {
const { list: { players } } = table[i]
players.forEach(({ awards: { type }}) => types.push(type))
}
console.log(types);
It will be cleaner to use forEach.
You will need 2 forEach due to your data structure.
But below code will:
check if awards is null
check if awards.type is null
const data = {
"team": {
"table": [
{
"cityCode": 123,
"list": {
"players": [
{
"name": "peter",
"school": "x",
"awards": {
"type": "gold"
},
"year": 2019
}
]
}
},
{
"cityCode": 456,
"list": {
"players": [
{
"name": "Dave",
"school": "y",
"awards": {
"type": "silver"
},
"year": 2018
},
{
"name": "Dave",
"school": "y",
"awards": {
"type": "gold"
},
"year": 2016
}
]
}
},
{
"cityCode": 444,
"list": {
"players": [
{
"name": "James",
"school": "y",
"awards": {
"type": null
},
"year": 2016
}
]
}
},
{
"cityCode": 555,
"list": {
"players": [
{
"name": "Name 101",
"school": "y",
"awards": {
"type": "platinum"
},
"year": 2016
},
{
"name": "Name 102",
"school": "y",
"awards": {
"type": null
},
"year": 2016
},
{
"name": "Name 103",
"school": "y",
"awards": null,
"year": 2016
},
]
}
}
]
}
}
// Expanded your data with more items
const data1 = data.team.table;
let types = []
data1.forEach((item, index) => {
item.list.players.forEach((player) => {
const awards = player.awards;
if (awards !== null && awards.type !== null) {
types = [...types, awards.type];
}
})
})
// Get the list of types
console.log(types);
// Get unique list of types
let unique_types = [...new Set(types)]
console.log(unique_types);

Creating a new array from the parents of array child element

I have the following array
{
"id": "111",
"name": "1111",
"children": [
{
"id": "22222",
"name": "2222",
"children": [
{
"id": "AAAA",
"name": "AAAA",
"children": [
{
"id": "DDD",
"name": "DDD"
},
{
"id": "EEE",
"name": "EEE"
}
]
},
{
"id": "BBBB",
"name": "BBB",
"children": [
{
"id": "FFF",
"name": "FFF"
},
{
"id": "GGG",
"name": "GGG",
"children": [
{
"id": "7777",
"name": "7777"
},
{
"id": "8888",
"name": "8888"
}
]
}
]
}
]
}
]
}
And I would like to create an array with the parents of a child by its ID.
So for example if I wanted to get the path to the child with ID "FFF", then the array would look like something like this:
["1111", "2222", "BBB", "FFF"]
How could I go about doing that?
You could take an iterative and recursive approach.
function getItems({ children, ...object }, key, value) {
var temp;
if (object[key] === value) return [object];
if (children) children.some(o => temp = getItems(o, key, value));
return temp && [object, ...temp];
}
var data = { id: "111", name: "1111", children: [{ id: "22222", name: "2222", children: [{ id: "AAAA", name: "AAAA", children: [{ id: "DDD", name: "DDD" }, { id: "EEE", name: "EEE" }] }, { id: "BBBB", name: "BBB", children: [{ id: "FFF", name: "FFF" }, { id: "GGG", name: "GGG", children: [{ id: "7777", name: "7777" }, { id: "8888", name: "8888" }] }] }] }] };
console.log(getItems(data, 'id', 'FFF'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could implement a recursive search to find all paths and return the correct one when you reach the desired name-value pair.
const isObject = (obj) => obj === Object(obj);
let data = loadData();
let expected = [ '1111', '2222', 'BBB', 'FFF' ];
let actual = findPath(data, 'name', 'FFF');
console.log(JSON.stringify(expected) === JSON.stringify(actual));
function findPath(data, key, value, includeIndicies=false) {
let opts = { found : null, includeIndicies : includeIndicies };
findPathInternal(data, key, value, opts, []);
return opts.found;
}
function findPathInternal(node, key, val, opts, path) {
if (Array.isArray(node)) {
for (let i = 0; i < node.length; i++) {
findPathInternal(node[i], key, val, opts, opts.includeIndicies ? path.concat(i) : path);
}
} else if (isObject(node)) {
if (node[key] === val) {
opts.found = path.concat(val); return; // Exit
} else {
let keys = Object.keys(node);
for (let i = 0; i < keys.length; i++) {
findPathInternal(node[keys[i]], key, val, opts, path.concat(node[key]));
}
}
}
};
function loadData() {
return {
"id": "111",
"name": "1111",
"children": [{
"id": "22222",
"name": "2222",
"children": [{
"id": "AAAA",
"name": "AAAA",
"children": [{
"id": "DDD",
"name": "DDD"
},
{
"id": "EEE",
"name": "EEE"
}
]
},
{
"id": "BBBB",
"name": "BBB",
"children": [{
"id": "FFF",
"name": "FFF"
},
{
"id": "GGG",
"name": "GGG",
"children": [{
"id": "7777",
"name": "7777"
},
{
"id": "8888",
"name": "8888"
}
]
}
]
}
]
}]
};
}
.as-console-wrapper { top: 0; max-height: 100% !important; }

Creating custom json objects using changeable number of variables

I have this json object:
[
{
"trip": [
{
"place": {
"id": 8,
"name": "New York",
},
"group": 1
}
},
...
]
I can have a N number of groups, so i need to create an array of objects like this:
[
{
"group1": "New York",
"group2": "Los Angeles",
"group3": "Rome",
...
}
]
Each group is called groupX where X is a number taken from every "group" instance in every object in the first array and his value must be taken from "name" instance in every object in the first array.
How can i do it? Thanks in advance!
.map() is handy for getting an array of results, applying a function to each element of an existing array. Something like this:
var obj = [{
"trip": [{
"place": {
"id": 8,
"name": "New York",
},
"group": 1
}]
},
{
"trip": [{
"place": {
"id": 8,
"name": "london",
},
"group": 2
}]
},
{
"trip": [{
"place": {
"id": 8,
"name": "tokyo",
},
"group": 3
}]
},
]
var result = obj.map((array_element) => {
let group = "group" + String(array_element['trip'][0]['group'])
let place = String(array_element['trip'][0]['place']['name'])
let to_return = {};
to_return[group] = place
return to_return;
})
console.log(result)
/*
[
{ "group1": "New York" },
{ "group2": "london" },
{ "group3": "tokyo" }
]
*/
You can do it like this:
let data = [
{ 'trip': [{ 'place': { 'id': 8, 'name': 'New York' }, 'group': 1 }] },
{ 'trip': [{ 'place': { 'id': 9, 'name': 'Los Angeles' }, 'group': 2 }] },
{ 'trip': [{ 'place': { 'id': 10, 'name': 'Rome' }, 'group': 3 }] },
]
let grouped = data.reduce(function(grouped, current) {
grouped['group' + current.trip[0].group] = current.trip[0].place.name;
return grouped;
}, {})
console.log(grouped);

filter an array of nested objects in 5 level

I am trying filter this array with .filter.
var objList = [
{
"name": "Object0Name",
"id": "Object0ID",
"Object1List": [
{
"id": "Object1id_A1",
"name": "Object1Name_A1",
"Object2List": [
{
"id": 187,
"name": "Object2Name_A1",
"Object3List": [
{
"id": "mammal",
"name": "mammal",
"Object4List": [
{
"id_client": "rabbit",
"Currency": "EUR"
},
{
"id_client": "cat",
"Currency": "EUR",
},
{
"id_client": "tiger",
"Currency": "EUR",
}
]
}
]
}
]
},
{
"id": "Object1id_B1",
"name": "Object1Name_B1",
"Object2List": [
{
"id": 189,
"name": "Object2Name_B1",
"Object3List": [
{
"id": "fish",
"name": "fish",
"Object4List": [
{
"id_client": "tiger shark",
"Currency": "EUR",
},
{
"id_client": "tuna",
"currency": "GBP",
},
]
}
]
}
]
}
]
}
]
var response= objList.filter(function(Object0List){
return Object0List.Object1List.filter(function(Object1List){
return Object1List.Object2List.filter(function(Object2List){
return Object2List.Object3List.filter(function(Object3List){
return Object3List.Object4List.filter(function(Object4List){
return Object4List.id_client==="tiger shark";
});
});
});
});
});
var myJSON = JSON.stringify(response);
console.log('The animal is:');
console.log(myJSON);
But the filter doesn't work. I am receiving all objects. I must receive:
[
{
"name": "Object0Name",
"id": "Object0ID",
"Object1List": [
{
"id": "Object1id_B1",
"name": "Object1Name_B1",
"Object2List": [
{
"id": 189,
"name": "Object2Name_B1",
"Object3List": [
{
"id": "fish",
"name": "fish",
"Object4List": [
{
"id_client": "tiger shark",
"Currency": "EUR",
}
]
}
]
}
]
}
]
}
]
Could someone help me find out what I'm doing wrong? I'm sure the problem is that I'm using the .filter function badly but it took several hours and I'm not capable of fixing it. I think that I do not understand this function for nested objects, I tried to filter the array of nested objects with lambda expressions but I'm also not able.
Thanks you very much.
You could check each property which is an array and take only filtered values.
This approach mutates the original array.
function filter(array, value) {
var temp = array.filter(o =>
Object.keys(o).some(k => {
var t = filter(Array.isArray(o[k]) ? o[k] : [], value);
if (o[k] === value) {
return true;
}
if (t && Array.isArray(t) && t.length) {
o[k] = t;
return true;
}
})
);
if (temp.length) {
return temp;
}
}
var array = [{ name: "Object0Name", id: "Object0ID", Object1List: [{ id: "Object1id_A1", name: "Object1Name_A1", Object2List: [{ id: 187, name: "Object2Name_A1", Object3List: [{ id: "mammal", name: "mammal", Object4List: [{ id: "rabbit", Currency: "EUR" }, { id: "cat", Currency: "EUR" }, { id: "tiger", Currency: "EUR" }] }] }] }, { id: "Object1id_B1", name: "Object1Name_B1", Object2List: [{ id: 189, name: "Object2Name_B1", Object3List: [{ id: "fish", name: "fish", Object4List: [{ id: "tiger shark", Currency: "EUR" }, { id: "tuna", currency: "GBP" }] }] }] }] }],
result = filter(array, 'tiger shark');
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I assume that every of your objects has this structure:
{
id: "sth",
name: "whatever"
children: [ /***/ ]
}
So then it is quite easy to filter recursively:
function filter(arr, search){
const result = [];
for(const {name, id, children} of arr){
children = filter(children, search);
if(children.length || id === search)
result.push({id, name, children });
}
return result;
}
Usable as:
var response = filter(objList, "tiger shark");

take JSON input and store it in to an array (javascript)

I have a JSON input of
{
"Categories": {
"Facets": [{
"count": 1,
"entity": "Company",
"Company": [{
"entity": "Ford Motor Co",
"Ford_Motor_Co": [{
"count": 1,
"entity": "Ford"
}]
}]
}, {
"count": 4,
"entity": "Country",
"Country": [{
"entity": "Germany",
"Germany": [{
"count": 1,
"entity": "Germany"
}],
"currency": "Euro (EUR)"
}, {
"entity": "Italy",
"Italy": [{
"count": 1,
"entity": "Italy"
}],
"currency": "Euro (EUR)"
}, {
"entity": "Japan",
"Japan": [{
"count": 1,
"entity": "Japan"
}],
"currency": "Yen (JPY)"
}, {
"entity": "South Korea",
"South_Korea": [{
"count": 1,
"entity": "South Korea"
}],
"currency": "Won (KRW)"
}]
}, {
"count": 5,
"entity": "Persons",
"Persons": [{
"count": 2,
"entity": "Dodge"
}, {
"count": 1,
"entity": "Dodge Avenger"
}, {
"count": 1,
"entity": "Major League"
}, {
"count": 1,
"entity": "Sterling Heights"
}]
}]
}
}
I need to get the values for entity in each level in to an array..
[Company, Ford Motor Co, Ford, ....... , Sterling Heights]
I am able to get thru the first level with the code
for (var k in h.Categories.Facets)
{
alert(h.Categories.Facets[k].entity);
}
How do I reach thru the innerlevels to get the values of entity??
You should do a foreach on each entity. If you know how many levels there are you can nest loops. If not - probably should go for a recursive function.
Edit
Recursive function:
function getEntities(ent)
{
alert(ent);
for (var l in ent)
{
getEntities(ent[l].entity);
}
}
Then use:
for (var k in h.Categories.Facets)
{
getEntities(h.Categories.Facets[k]);
}
Good luck!
The most general recursive answer:
function getEntities(any) {
var entities = [];
if ('entity' in any​) {
entities.push(any.entity);
}
for (var prop in any) {
if (any[prop] instanceof Object && any.hasOwnProperty(prop)) {
entities.append(getEntities(any[prop]));
}
}
return entities;
}
console.log(getEntities(h));
The line:
if (any[prop] instanceof Object && any.hasOwnProperty(prop)) {
Keeps numbers/nulls from bombing and the any.hasOwnProperty(prop) makes up for frameworks that like to attach to the Object prototype.
One way is to write a recursive method that accepts an array and extracts the methods from it.
As suggested, you can use a recursive function to loop through every possible nested combo:
var allEntities = [];
function getEntities(obj)
{
if (obj != null)
{
var entityName = obj["entity"];
alert(entityName);
if (entityName != null)
{
allEntities.push(entityName);
var adjName = entityName.replace(/ /gi, "_");
var entities = obj[adjName];
if (entities != null)
{
for (var e in entities)
{
var innerEntities = getEntities(entities[e]);
for (var inner in innerEntities)
allEntities.push(innerEntities[inner]);
}
}
}
}
}
for (var k in h.Categories.Facets)
{
alert(h.Categories.Facets[k].entity);
getEntities(h.Categories.Facets[k]);
}
alert(allEntities.length);

Categories

Resources