How to search JSON as a path - javascript

I have a JSON as shown below
{
"name": "Soft Drinks",
"T2": [
{
"name": "Bottled",
"T3": [
{
"name": "Apple",
"leaf": [
{
"name": "Apple 500 ML"
},
{
"name": "Apple 1 Ltr"
}
]
}
]
},
{
"name": "Fountain",
"T3": [
{
"name": "Apple",
"leaf": [
{
"name": "Apple Regular, 500 ML"
}
]
}
]
},
{
"name": "Tin",
"T3": [
{
"name": "Apple",
"leaf": [
{
"name": "Apple Regular, 300 ML"
}
]
}
]
}
]
}
I will get a Input String as Path means this way as an array
1st case :
Input : Soft Drinks,Bottled
Output : ["Apple"]
2nd case :
Input : Soft Drinks,Fountain,Apple
Output
[
"leaf",
[
{
"name": "Apple Regular, 500 ML"
}
]
]
Could anybody please help me how to resolve this?
I am forming this input array when navigated through the Jquery Accordian, so depending on the click, I need to show the respective data.
For your reference the screen looks this way

Some changes: every node has keys name and leaf,
var drinks = [
{
"name": "Soft Drinks",
"leaf": [
{
"name": "Bottled",
"leaf": [
{
"name": "Apple",
"leaf": [
{
"name": "Apple 500 ML"
},
{
"name": "Apple 1 Ltr"
}
]
}
]
},
{
"name": "Fountain",
"leaf": [
{
"name": "Apple",
"leaf": [
{
"name": "Apple Regular, 500 ML"
}
]
}
]
},
{
"name": "Tin",
"leaf": [
{
"name": "Apple",
"leaf": [
{
"name": "Apple Regular, 300 ML"
}
]
}
]
}
]
}
];
var drinkParser = (function () {
function DrinksCollection(nodes) {
extend(this, nodes);
this.length = nodes.length;
this.parent = null;
}
/*
* Method extend from Underscore.js 1.6.0
* http://underscorejs.org
* (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
* Underscore may be freely distributed under the MIT license.
*/
function extend(obj) {
Array.prototype.slice.call(arguments, 1).forEach(function (source) {
if (source) {
for (var prop in source) {
obj[prop] = source[prop];
}
}
});
return obj;
}
function nodeMatcher(node, selectorExpr) {
var type = typeof selectorExpr;
if (type === "string") {
return node.name === selectorExpr;
} else if (type === "function") {
return !!selectorExpr(node);
} else {
throw new TypeError("Cannot filter node with selector " + selectorExpr);
}
}
DrinksCollection.prototype = {
toArray: function () {
return Array.prototype.slice.call(this);
},
find: function (selectorExpr) {
var foundNodes = [];
this.walk(function (node) {
if (nodeMatcher(node, selectorExpr)) {
foundNodes.push(node);
}
});
return this.pushStack(foundNodes);
},
each: function (callback) {
this.toArray().forEach(callback);
return this;
},
map: function (callback) {
return this.toArray().map(callback);
},
walk: function (callback) {
function walk(node, index) {
callback.call(node, node, index);
(node.leaf || []).forEach(walk);
}
this.each(walk);
return this;
},
pushStack: function (nodes) {
var newInstance = new DrinksCollection(nodes);
newInstance.parent = this;
return newInstance;
},
children: function () {
var allChildren = [];
this.each(function (node) {
allChildren = allChildren.concat(node.leaf);
});
return this.pushStack(allChildren);
},
props: function (propName) {
return this.map(function (node) {
return node[propName];
});
},
names: function () {
return this.props("name");
}
};
return function (nodes) {
return new DrinksCollection(nodes);
}
}());
(function parseDrinks() {
console.log(drinkParser(drinks).find('Soft Drinks').find('Bottled').children().names()); // [ 'Apple' ]
console.log(drinkParser(drinks).find('Soft Drinks').find('Bottled').find('Apple').children().map(function (drink) {
return drink;
})); // [ { name: 'Apple 500 ML' }, { name: 'Apple 1 Ltr' } ]
}());

Related

How to check if array is empty or not and return value inside foreach in angular8

I have below data,
What i want to do is, i need to check values[] array in each object,
if it is empty then return true, else if values[] array has some record, it will return false.
i have created function for this, but it is reuturning false everytime.
if it is true thn i want to hide table. Table are multiple not single one, so if values arrayis empty thn only want to hide particular table.
{
"records": [
{
"context": {
"team": [
"MYTEAM-Consume-TEAM-SETUP-ADMIN"
]
},
"values": []
},
{
"context": {
"team": [
"TEAM1"
]
},
"values": [
{
"value": "red",
"label": "dd"
}
]
},
{
"context": {
"team": [
"Test"
]
},
"values": []
},
]
}
Code
hideContextTable(rows) {
const data = rows;
if (data.records) {
data.records.forEach(function (record) {
if (record.values.length === 0) {
return true;
}
});
}
return false;
}
deleteAllContextData(data) {
const tbl = this.hideContextTable(data);
console.log(tbl,"tbl");
if (tbl) {
this.showContextTables = false;
}
}
Simply check the length of returned data from filter function.
data = {
"records": [
{
"context": {
"team": [
"MYTEAM-Consume-TEAM-SETUP-ADMIN"
]
},
"values": []
},
{
"context": {
"team": [
"TEAM1"
]
},
"values": [
{
"value": "red",
"label": "dd"
}
]
},
{
"context": {
"team": [
"Test"
]
},
"values": []
},
]
};
function hideContextTable(data) {
const result = data.records.filter(record => record.values.length);
const flag = result.length ? true : false;
console.log(flag)
}
hideContextTable(data);
return in the higher order function of forEach will not cause flow to leave the hideContextTable function. You should use a variable that is accessible from outside that function and set it if the condition is met, then return that variable at the end of the function.
const rows = {
"records": [
{
"context": {
"team": [
"MYTEAM-Consume-TEAM-SETUP-ADMIN"
]
},
"values": []
},
{
"context": {
"team": [
"TEAM1"
]
},
"values": [
{
"value": "red",
"label": "dd"
}
]
},
{
"context": {
"team": [
"Test"
]
},
"values": []
},
]
}
function hideContextTable(rows) {
let isEmpty = false;
const data = rows;
if (data.records && data.records.values) {
data.records.forEach(function (record) {
if (record.values.length === 0) {
isEmpty = true;
return; // this is a higher order function
// meaning: it won't leave the context of hideContextTable
}
});
}
return isEmpty;
}
const test = hideContextTable(rows);
console.log(test);

Unexpected data sorting result after filtering from parent and child node

I have list of tree node metadataList Like below:
[
{
"data": {
"metadata": {
"category": [
"Csp"
]
}
},
"children": [
{
"data": {
"metadata": {
"category": [
"Csp"
]
}
},
"children": [
]
},
{
"data": {
"metadata": {
"category": [
"Mpn"
]
}
},
"children": [
]
},
{
"data": {
"metadata": {
"category": [
"Mpn"
]
}
},
"children": [
]
},
{
"data": {
"metadata": {
"category": [
"Mpn"
]
}
},
"children": [
]
}
]
},
{
"data": {
"metadata": {
"category": [
"Isv"
]
}
},
"children": [
{
"data": {
"metadata": {
"category": [
"Isv"
]
}
},
"children": [
]
},
{
"data": {
"metadata": {
"category": [
"Isv"
]
}
},
"children": [
]
}
]
},
{
"data": {
"metadata": {
"category": [
"Csp"
]
}
},
"children": [
{
"data": {
"metadata": {
"category": [
"Csp"
]
}
},
"children": [
]
}
]
},
{
"data": {
"metadata": {
"category": [
"Mpn"
]
}
},
"children": [
{
"data": {
"metadata": {
"category": [
"Mpn"
]
}
},
"children": [
]
},
{
"data": {
"metadata": {
"category": [
"Mpn"
]
}
},
"children": [
]
},
{
"data": {
"metadata": {
"category": [
"Mpn"
]
}
},
"children": [
]
},
{
"data": {
"metadata": {
"category": [
"Csp"
]
}
},
"children": [
]
},
{
"data": {
"metadata": {
"category": [
"Isv"
]
}
},
"children": [
]
}
]
},
{
"data": {
"metadata": {
"category": [
"Incentives"
]
}
},
"children": [
{
"data": {
"metadata": {
"category": [
"Incentives"
]
}
},
"children": [
]
}
]
}
]
Which is a type of array of data and children collection it's class like below:
export default class CurrentTopicMetadataTreeNode {
public data: CurrentTopicMetadata;
public children: CurrentTopicMetadataTreeNode[];
}
export default class CurrentTopicMetadata {
public id: string;
public metadata: TopicMetadata
}
export class TopicMetadata {
public category: Category[]
}
export enum Category {
Csp = 'Csp',
Mpn = 'Mpn',
Incentives = 'Incentives',
Referrals = 'Referrals',
Isv = 'Isv',
}
What I am trying, to filter list as data and children order as per category. Let say if filter by a category all data and children belongs to that category should come like below order.
But I am getting data like this order :
One Element On Array Problem Set:
Here in this array if I search with Csp Only data in root node which is Csp and data in children only has one data which contains Csp would be in array.
[{
"data": {
"metadata": {
"category": [
"Csp"
]
}
},
"children": [
{
"data": {
"metadata": {
"category": [
"Csp"
]
}
},
"children": [
]
},
{
"data": {
"metadata": {
"category": [
"Mpn"
]
}
},
"children": [
]
},
{
"data": {
"metadata": {
"category": [
"Mpn"
]
}
},
"children": [
]
},
{
"data": {
"metadata": {
"category": [
"Mpn"
]
}
},
"children": [
]
}
]
}]
Expected Output: So after filtered by Csp node should be look like this:
[
{
"data": {
"metadata": {
"category": [
"Csp"
]
}
},
"children": [
{
"data": {
"metadata": {
"category": [
"Csp"
]
}
},
"children": [
]
}
]
}
]
here is my code, where I am doing wrong?
// Rule 1 check parent metadata category whether be empty
// Rule 2 and 3
function find_in_children(children, parent_category) {
children_has_same_category = []
for(var i in children) {
let child = children[i];
if(child.children != undefined && child.children.length > 0 && child.data.metadata.category == parent_category) {
children_has_same_category.push(child);
}
}
if(children_has_same_category.length > 0) {
return children_has_same_category
} else {
for(var i in children) {
let child = children[i];
return find_in_children(child.children, parent_category);
}
}
}
function check_object(object) {
let parent_category = object.data.metadata.category[0];
if(object.children != undefined && object.children.length > 0) {
return {'data': object.data, 'children': find_in_children(object.children, parent_category)}
} else {
return {'data': object.data}
}
}
function apply_rules(object) {
// Rule 1 check parent metadata category whether be empty
if(object.data.metadata.category.length > 0) {
return {'data': object.data}
} else {
return check_object(object)
}
}
target = {
value: 'Isv'
}
filtered_datas = []
for(var i in datas) {
let data = datas[i];
if(data.data.metadata.category.length > 0) {
result = apply_rules(data)
if(result.data.metadata.category[0] == target.value) {
filtered_datas.push(result);
}
}
}
Here is the data sample and result: https://jsfiddle.net/faridkiron/b02cksL8/#&togetherjs=F7FK3fBULx
I have resolve above problem like below way:
const findMatchedChild = (nodeInList, type) => {
const node = Object.assign({}, nodeInList)
node.children = nodeInList.children
.filter(child => child.data.metadata.category.includes(type))
.map(child => findMatchedChild(child, type))
return node
}
const cspList = findMatchedChild({ children: data }, 'Csp').children
console.log(JSON.stringify(cspList, null, 2));
Got the expected result.

node js create an object in specific pattern from array of object

I'm facing some issue in for loop while creating an object from array of object.I have an array as this in node js app:
[
{
"Material": "113/133",
"Name": [
{
"name": "WELD1",
"value": 27520
},
{
"name": "WELD2",
"value": 676992
},
{
"name": "WELD3",
"value": 421
}
]
},
{
"Material": "150/300",
"Name": [
{
"name": "WELD1",
"value": 1441
},
{
"name": "WELD2",
"value": 555
},
{
"name": "WELD3",
"value": 100992
}
]
}
]
I want to return object like this which contains all the Material as array, Name and there value in array of object like this:
{
Material: ["113/133", "150/300"],
datasets: [
{
label: "WELD1",
data: [27520,1441]
},
{
label: "WELD2",
data: [676992,555]
},
{
label: "WELD3",
data: [100,20,0]
}
]
}
I want to get result using for loop.
you can use .reduce() and do something like this:
var arr = [
{
"Material": "113/133",
"Name": [
{
"name": "WELD1",
"value": 27520
},
{
"name": "WELD2",
"value": 676992
},
{
"name": "WELD3",
"value": 421
}
]
},
{
"Material": "150/300",
"Name": [
{
"name": "WELD1",
"value": 1441
},
{
"name": "WELD2",
"value": 555
},
{
"name": "WELD3",
"value": 100992
}
]
}
];
var newArr = arr.reduce((acc, ob) => {
for (var key in ob)
if(typeof acc[key] === 'object')
acc[key] = acc[key] ? acc[key].concat(ob[key]) : [ob[key]];
else
acc[key] ? acc[key].push(ob[key]) : acc[key] = [ob[key]];
return acc;
}, {});
console.log(newArr);
let array = [
{
"Material": "113/133",
"Name": [
{
"name": "WELD1",
"value": 27520
},
{
"name": "WELD2",
"value": 676992
},
{
"name": "WELD3",
"value": 421
}
]
},
{
"Material": "150/300",
"Name": [
{
"name": "WELD1",
"value": 1441
},
{
"name": "WELD2",
"value": 555
},
{
"name": "WELD3",
"value": 100992
}
]
}
]
let answer = {Material: [], datasets: []}
array.forEach(x => {
answer.Material.push(x.Material);
x.Name.forEach(na => {
let object = answer.datasets.find(obj => obj.label === na.name) || {label: "", data: []};
if(object.label === ""){
object.label = na.name;
object.data.push(na.value);
answer.datasets.push(object);
}else{
object.data.push(na.value)
}
});
});
console.log(answer);
The above is alternative solution using forEach instead of reduce
Use of Array.reduce to build your new data structure using data you have
const start = [{
"Material": "113/133",
"Name": [{
"name": "WELD1",
"value": 27520
},
{
"name": "WELD2",
"value": 676992
},
{
"name": "WELD3",
"value": 421
}
]
},
{
"Material": "150/300",
"Name": [{
"name": "WELD1",
"value": 1441
},
{
"name": "WELD2",
"value": 555
},
{
"name": "WELD3",
"value": 100992
}
]
}
];
const end = start.reduce((tmp, {
Material,
Name,
}) => {
// Handle the material
// If it do not exist in the array, push it
if (!tmp.Material.includes(Material)) {
tmp.Material.push(Material);
}
// Handle the datasets
// Look at each Name
Name.forEach(({
name,
value,
}) => {
// Can we find the label?
const labelFind = tmp.datasets.find(y => y.label === name);
// If we can't find the label, create a new dataset
if (!labelFind) {
tmp.datasets.push({
label: name,
data: [
value,
],
});
return;
}
// If we has found it push new value in the dataset
labelFind.data.push(value);
});
return tmp;
}, {
Material: [],
datasets: [],
});
console.log(end);
// This is the old fashioned way.
// Iterate over whole array,
// make a map, push value where 'name' is found in map
// later iterate over this map - dataMap - and form required datasets array.
var Material = [];
var dataMap = {};
arr.forEach(obj => {
Material.push(obj.Material);
obj.Name.forEach(item => {
if(dataMap[item.name]){
dataMap[item.name].push(item.value);
}
else {
dataMap[item.name] = [item.value];
}
});
});
var datasets = [];
Object.keys(dataMap).forEach(label => {
datasets.push({
label: label,
data: dataMap[label]
});
});
var result = {
Material: Material,
datasets: datasets
}
console.log(result);

Check for more than one key value pair in JSON data

I have JSON data formatted with multiple products which each have multiple variants and has data showing if the variant is available as well as the size of the variant.
"products":[
{
"variants":[
{
"available":true,
"selectedOptions":[
{
"name":"Size",
"value":"M"
}
]
},
{
"available":true,
"selectedOptions":[
{
"name":"Size",
"value":"L"
}
]
}
]
},
{
"variants":[
{
"available":true,
"selectedOptions":[
{
"name":"Size",
"value":"S"
}
]
},
{
"available":false,
"selectedOptions":[
{
"name":"Size",
"value":"L"
}
]
}
]
}
]
I want to traverse the JSON data and tell if the size of the product variant is large ("value":"L") and if the product is available ("available":true). I'm able to check for one or the other but I'm not sure how to check both at the same time. This is what I have so far:
o = products;
function traverse(o) {
for (var i in o) {
if(o[i] == true){
console.log([i,o[i]]);
}
if(o[i] == 'L'){
console.log([i,o[i]]);
}
if (o[i] !== null && typeof(o[i])=="object") {
traverse(o[i]);
}
}
}
console.log(o);
traverse(o);
}
You can use this approach to loop over the possible variants per product and identify the target according to availability and size.
This approach uses the function find and forEach for traversing the Object.
The function find looks for a selectedOption with size L and the nested forEach to check the availability.
var obj = { "products": [{ "variants": [{ "available": true, "selectedOptions": [{ "name": "Size", "value": "M" }] }, { "available": true, "selectedOptions": [{ "name": "Size", "value": "L" }] } ] }, { "variants": [{ "available": true, "selectedOptions": [{ "name": "Size", "value": "S" }] }, { "available": false, "selectedOptions": [{ "name": "Size", "value": "L" }] } ] } ]};
obj.products.forEach((p, i) => {
p.variants.forEach((v) => {
if (v.available) {
var found = v.selectedOptions.find((s) => s.value === 'L');
if (found) {
console.log(`Found a product at index '${i}' with variant ['${v.available}' | '${found.value}']`);
}
}
});
});
.as-console-wrapper { max-height: 100% !important; top: 0; }

Merge and match objects that match AngularJS

Hello I would like to merge and match two json objects. I would like to do with the Angular way. Any help would be greatly appreciated. Thank you everyone. Here is an example:
// First json object
{
"UserID":"john.davis",
"edpi":null,
"UserApp":[
{
"Name":"name3",
"Link":"/Protect3.php"
},
{
"Name":"name5",
"Link":"/Admin/Launch.html"
},
{
"Name":"name2",
"Link":"aaap/defaultBH.php"
}
]
}
// Second json object
[
{
"color":"color1",
"icon": "content/images/icons/administrator.svg",
"Name": "name5"
},
{
"color":"color3",
"icon": "content/images/icons/administrator.svg",
"Name": "name3"
},
{
"color":"color2",
"icon": "content/images/icons/behavior.svg",
"Name": "name2"
}
]
Hoping to get this
[
{
"TokenName":"name3",
"TokenLink":"../Protect3.asp",
"color":{
"color":"color3",
"icon": "content/images/icons/administrator.svg",
"match": "name3"
}
},
{
"Name":"name5",
"Link":"/Admin/Launch.html",
"color":{
"color":"color1",
"icon": "content/images/icons/administrator.svg",
"match": "name5"
}
},
{
"Name":"name2",
"Link":"aaap/defaultBH.php"
"color":{
"color":"color2",
"icon": "content/images/icons/behavior.svg",
"match": "name2"
}
}
]
I figured it out. Here is the code below.
function mergeArraysOnProperty(array1, array2, property) {
array1.forEach(function(element1) {
array2.forEach(function(element2) {
if (element1[property] === element2[property]) {
for (var newProperty in element2) {
if (element2.hasOwnProperty(newProperty)) {
element1[newProperty] = element2[newProperty];
}
}
}
});
});
return array1;
};
$scope.newjson = mergeArraysOnProperty(userapp, color, "Name");
console.log($scope.newjson);

Categories