How to search the specific text in a nested object? - javascript

For example, I have a JSON array as follow, I will show it on a treelist ,so that user can select the item.Now there is a feature that allowing the user to search the specific text in the list. And the list is a nested list.I hava bind the JSON array to a treelist using HTML and Javascript. Now I should find a good way to search the specific text, and then return the object that include parent node when I find the text.
var allcategories=
[
{
"name": "shoes",
"subcategories": [
{
"name": "man's shoes",
"subcategories": [
{
"name": "sample name"
},
{
"name": "sample name",
"subcategories": []
}
]
},
{
"name": "woman's shoes",
"subcategories": []
}
]
},
{
"name": "cars",
"subcategories": [
{
"name": "cars1",
"subcategories": [
{
"name": "sample name"
},
{
"name": "sample name",
"subcategories": []
}
]
}
]
}
];
So now I want to use the indexOf() function to search the result. If the search is successful, the program should just return the parent category.Anyone have the same question in developing the codes?

As Bergi suggests, you can use a recursive function:
function getCategory(categories, name) {
var category, result;
for (var i=0, iLen=categories.length; i<iLen; i++) {
category = categories[i];
if (category.name == name) {
return category;
} else if (category.subcategories) {
result = getCategory(category.subcategories, name);
if (result) return result;
}
}
// return undefined if category name not found
}
Note that this will return the first subcategory with a matching name, so the name needs to be unique.

#Leeli - you can use this JS lib; DefiantJS (http://defiantjs.com) with which searches in JSON structures becomes a trivial thing. The lib extends the global object JSON with the method "search". With this method, you can search with XPath expressions and it will retusn an array with the matches (empty array if no matches were found). See the example code below.
Here is a working fiddle:
http://jsfiddle.net/hbi99/wXfE6/
var data = [
{
"name": "shoes",
"subcategories": [
{
"name": "man's shoes",
"subcategories": [
{ "name": "heels" },
{ "name": "loafers" }
]
},
{ "name": "woman's shoes" }
]
},
{
"name": "cars",
"subcategories": {
"name": "cars1",
"subcategories": [
{ "name": "Sedan" },
{ "name": "SUV" }
]
}
}
],
res = JSON.search( data, '//*[name="cars1"]/subcategories' );
console.log( res[0].name );
// Sedan

hi here is a jquery function i wrote. since its a associated list in .net the child list is an empty object when there are no child nodes so i have to check is empty instead of simply looking for it to be null. should be backwards compatible though and work as a solution to this question
function findCategory(categories, categoryId)
{
var ret = null;
$.each(categories, function (e, v) {
if (e === categoryId)
ret = v;
else if (!jQuery.isEmptyObject(v.children))
ret = findCategory(v.children, categoryId);
return !ret; //break out of loop when ret not null
});
return ret;
}

Related

How do I convert JSON values into a key value pair pir

I have a JSON in the following format and need to convert the 2 values into a Key / Value pair in javascript
"column_values": [
{
"id": "status",
"text": "Working on it"
}
]
I need the result to be
"column_values"[{"status": "Working on it"}]
I need the code to iterate through the column_values array and convert all the sets of id and text pairs to the key = id value : Value = text:values
Is my result possible?
Additional Information...
I am parsing a response from monday.com api in zapier.
the api payload is contained in
const results = response.json;
the full api payload is
{
"data": {
"boards": [
{
"name": "Test Board",
"items": [
{
"name": "Name Change",
"id": "625495642",
"column_values": [
{
"id": "person",
"text": ""
},
{
"id": "subitems",
"text": "Subitem 1, Subitem 2"
},
{
"id": "status",
"text": "Working on it"
},
{
"id": "dropdown",
"text": "Test1"
},
{
"id": "formula",
"text": ""
}
]
}
]
}
]
},
"account_id": 1111
}
I need to the the code to parse the data and replace the column_values with the format above, and then pass the reformated payload to
return results;
You just Map the Array you start out with to an Array with the values.
var column_values = [ { "id": "status", "text": "Working on it" } ]
var KeyValuePairs = column_values.map(cv => [cv.id,cv.text]);
console.log(KeyValuePairs);
If every object is going to contain the id and text keys only, you can map it and delete the other keys.
column_values = column_values.map(item => {
item[item.id] = item.text;
delete item.id;
delete item.text;
return item;
});
try this
var column_values = [ { "id": "status", "text": "Working on it" } ]
var res = column_values.map(x => ({[x.id] : x.text}))
console.log(res)

Formatting JSON data for Tree Graph

I have a JSON array of the following format (this data is pulled from mongodb to be displayed as a tree graph on a react-based front-end):
[
{
"name": "7654321",
"children": [
{
"_id": "LjYgocn9PsHhEFbM7",
"accountId": "4343213"
},
{
"_id": "sB2ipCstYnLnHrAuu",
"accountId": "4343271"
},
{
"_id": "JhugmhxS7A57Y34wM",
"accountId": "4343276"
}
]
},
{
"name": "4343213",
"children": [
]
},
{
"name": "4343271",
"children": [
{
"_id": "sie9mtttgdRw7Ktma",
"accountId": "4343279"
}
]
},
{
"name": "4343279",
"children": [
{
"_id": "sie23mtttgdRw7Ktma",
"accountId": "8765345"
}
]
},
{
"name": "4343276",
"children": [
]
}
]
The goal is to re-format (rename and delete some keys) this data to be used in react-tree-graph. From the sample above, output should look like:
[
{
"name": "7654321",
"children": [
{
"name": "4343213"
},
{
"name": "4343271",
"children": [
{
"name": "4343279",
"children": [
{
"name": "8765345"
}
]
}
]
},
{
"name": "4343276"
}
]
}
]
Any help is appreciated!
You could first create a Map that has as keys the name property values, and as corresponding values the (unfinished) result objects. They start off with just the name property.
Then you can iterate the children information in the input, to wire the children into the above mentioned result objects, which can be done efficiently using the name as key in the Map.
Whenever you wire a child object into a parent object, you know that child is not a top-level object in the final result. So starting with all nodes, you would trim that list (a Set) all those nodes that occur in a children array. This will leave you with only the top level nodes, which in its array form represents the desired output.
Implementation:
let data = [{"name": "7654321","children": [{"_id": "LjYgocn9PsHhEFbM7","accountId": "4343213"},{"_id": "sB2ipCstYnLnHrAuu","accountId": "4343271"},{"_id": "JhugmhxS7A57Y34wM","accountId": "4343276"}]},{"name": "4343213","children": []},{"name": "4343271","children": [{"_id": "sie9mtttgdRw7Ktma","accountId": "4343279"}]},{"name": "4343279","children": [{"_id": "sie23mtttgdRw7Ktma","accountId": "8765345"}]},{"name": "4343276","children": []}];
let map = new Map(data.map(({name, children}) => [name, { name }]));
let roots = new Set(map.values());
for (let {name, children} of data) {
if (!children?.length) continue;
map.get(name).children = children.map(({accountId}) => {
let child = map.get(accountId) || { name: accountId };
roots.delete(child);
return child;
});
}
let result = Array.from(roots);
console.log(result);

Group nested arrays in an object in js (Tree Vue.js)

This is my array of objects:
I am using vue.js , I need a tree like this to keep the structure of tree view: https://v2.vuejs.org/v2/examples/tree-view.html
[
{
"name": "",
"children": []
},
{
"name": "",
"children": [
{
"name": "Leggi",
"children": []
}
]
},
{
"name": "",
"children": [
{
"name": "Leggi",
"children": [
{
"name": "2010",
"children": []
}
]
}
]
},
{
"name": "",
"children": [
{
"name": "Leggi",
"children": [
{
"name": "2011",
"children": []
}
]
}
]
},
{
"name": "",
"children": [
{
"name": "Titoli",
"children": []
}
]
}
]
I need a function to retrive an object grouped by name with his childrens
{
"name": "",
"children": [
{
"name": "Leggi",
"children": [
{
"name": "2010",
"children": []
},
{
"name": "2011",
"children": []
}
],
"name": "Titoli",
"children": []
}
]
}
I would like to know if there it is a simple way (instead of writing a recursive function), like using lodash or something near it.
Thanks
I think that i have implemented a more readable answer:
const rootTree = [];
const putInTree = (tree, node) => {
let nodeInTree = tree.find(x => x.name === node.name);
if (!nodeInTree) {
nodeInTree = {name: node.name, children: []};
tree.push(nodeInTree);
}
if (node.children[0]) putInTree(nodeInTree.children, node.children[0])
}
nodes.forEach(node => putInTree(rootTree, node));
nodes here is your start array, let me know if this is ok
treeArchive.forEach(element => {
element.children.forEach(father => {
if (result.children.length != 0) {
cicleChildrens(result, father);
function cicleChildrens(padrePrecedente, nuovoPadre){
var brother = padrePrecedente.children.find(x => x.name == nuovoPadre.name);
if (brother != undefined) cicleChildrens(brother, nuovoPadre.children[0]);
else padrePrecedente.children.push(nuovoPadre);
};
}
else result.children.push(father);
});
});
This is currently my working code.. I'm struggling tryng to understand your code #chriss
Try this one:
function getGroupedByName(given) {
let result = given.reduce((a, b) => {
if(!a[b.name]) a[b.name] = [];
a[b.name] = [...a[b.name], ...b.children];
return a;
}, {});
result = Object.keys(result).map(key => ({name: key, children: getByName(result[key])}));
return result;
}
const o = []; // your initial object
getGroupedByName(o, "Leggi")
It is returning it as an array of objects having name and children props, as i am assuming first level can also have multiple different names, not all being ""
It goes first trough all elements in array and groups them into object with structure { name: children } where children is array of all children for same group.
For each children array it preforms same operation, going trough array and flattening it into { name: children } object.
At this moment we have following structure:
{ "": {
Leggi: {...}
}}
When everything is grouped, Object.keys loops trough all keys and breaks it into array where key is name and value children property

Lodash iterate nested loops

I am trying to iterate through below Collections JSON object. I am trying to find collection elements which have one of the tags from tagArray. Basically this is a filter exercise to have collection elements that have tags as selected from the tagArray.
{
1: {
"description": "AAA",
"tags": [
{
"name": "tag1",
},
{
"name": "tag2",
},
{
"name": "tag3",
},
],
"name": "XYZ",
},
2: {
"description": "BBB",
"tags": [
{
"name": "tag1",
}
],
"name": "CCC",
},
3: {
"description": "xms",
"tags": [],
"name": "Huo",
},
4: {
"description": "asd",
"tags": [],
"name": "TXS",
}
}
tagArray looks like this : [ tag1, tag2, ... ]
I have coded it as below using lodash and it works fine. But I am not sure if I can improve this further and how?
const filterByTags = (collections, filterTags) => {
let filteredCollections = _.pickBy(collections, (collection) => {
let collectionWithTag = false;
_.map(collection.tags, (collectionTag) => {
if (filterTags.indexOf(collectionTag.name) !== -1) {
collectionWithTag = true;
return collectionWithTag;
}
});
return collectionWithTag;
});
return filteredCollections;
};
You don't want to use pickBy but rather filter (Lodash/native)
You don't want to use map but rather some (Lodash/native)
You don't want to use indexOf but rather includes (Lodash/native)
function filterByTags(collections, filterTags) {
return _.filter(collections, collection => {
return _.some(collection.tags, collectionTag => {
return _.includes(filterTags, collectionTag.name);
});
});
}

How to iterate nested json object?

I have a nested JSON Object and i want to iterate that.
JSON Response
{
"specifications": {
"IP6": {
"name": "Devices",
"productSubType": "Device",
"productSpecificationType": "Phones"
}
},
"offers": {
"US-PRE-IPHONE-CASE": {
"path": "productDetails/IP6",
"familyData": {
"0": "Missing Family Level data Should be here"
},
"facets": [],
"type": [],
"offers": {
"US-PRE-HG-PH-IP6": {
"hashDigest": "cf23df2207d99a74fbe169e3eba035e633b65d94",
"offerName": "offerNameString",
"productName": "iPhone 6 Case Mate Naked Tough Case - Clear",
"productOfferings": {
"ratings": "4.5",
"noOfReviews": "2010"
},
"offerStatus": {},
"displayPriority": "100200",
"descriptions": {
"shortDescription": "Iphone Decription ",
"longDescription": "longDescriptionStri6 descriptionng",
"alternativeDescription": "alternativeDescriptionString",
"reprsentativeDescription": ""
},
"specifications": [
"someSpecificationId1"
],
"brand": "Apple",
"productType": "Device",
"productSubType": "Phone",
"offerType": "",
"offerSubType": "",
"compatibility": {},
"classification": [],
"images": {
"thumbanail": {
"imagePath": "http://s.tmocache.com/images/png/products/accessories/SUPM43270/SUPM43270-small.png"
}
},
"equipmentCharacteristics": {},
"offerVariants": {},
"type": "hard-good",
"offers": [],
"family": "IP6",
"pricePoints": {
"withServicePrice16GBNEW": {
"displayPriority": "1001",
"pricingMessage": "device price with service activation",
"price": "34.99",
"discounts": {}
}
},
"dynamicPricingData": {},
"inventoryData": {
"SKUGOLD16GBN": {
"availibility": "Pre-order now!",
"availableTimeline": ""
}
}
}
}
}
}
}
Now as you see there are nested JSON objects in this and I want the value of
productName
shortDescription
imagePath
availibility
What I have tried is
function change(){
var acc = response; //response is JSON Object mentioned above
var accArray = [];
var accArray1 = [];
for (var obj in acc.specifications){
accArray.push(obj);
}
alert(accArray[0]);
for (var obj in accArray[0].offers){
accArray1.push(obj);
}
alert(accArray1[0]);
}
I am able to get the first object the first alert output is
IP6
but when I am trying to iterarte the IP6 object in same way the output is
undefined
I want to fetch all the 4 values as I mentioned above and then put them in an array.
As Grundy pointed out in his comment, obj in your code is the key of properties/items in specifications object. That means 'obj' is just a string.
To get reference to the object, change your code as below:
for(var obj in acc.specifications){
accArray.push(acc.specifications[obj]);
}
For better readability change obj to key
You can use for..in loop and recursion.
function find(obj, fieldName){
if(Array.isArray(obj)){
for(var i=0, len=obj.length;i<len;i++){
var nested = find(obj[i],fieldName);
if(nested.isFind) return nested;
}
}else{
if(typeof obj !== "object") return {isFind:false};
for(var i in obj){
if(i === fieldName) return {isFind:true, value:obj[i]};
var nested = find(obj[i],fieldName);
if(nested.isFind) return nested;
}
}
return {isFind:false};
}
this function return object with field isFind for case when available value can be null or undefined
var obj = {
"specifications": {
"IP6": {
"name": "Devices",
"productSubType": "Device",
"productSpecificationType": "Phones"
}
},
"offers": {
"US-PRE-IPHONE-CASE": {
"path": "productDetails/IP6",
"familyData": {
"0": "Missing Family Level data Should be here"
},
"facets": [],
"type": [],
"offers": {
"US-PRE-HG-PH-IP6": {
"hashDigest": "cf23df2207d99a74fbe169e3eba035e633b65d94",
"offerName": "offerNameString",
"productName": "iPhone 6 Case Mate Naked Tough Case - Clear",
"productOfferings": {
"ratings": "4.5",
"noOfReviews": "2010"
},
"offerStatus": {},
"displayPriority": "100200",
"descriptions": {
"shortDescription": "Iphone Decription ",
"longDescription": "longDescriptionStri6 descriptionng",
"alternativeDescription": "alternativeDescriptionString",
"reprsentativeDescription": ""
},
"specifications": [
"someSpecificationId1"
],
"brand": "Apple",
"productType": "Device",
"productSubType": "Phone",
"offerType": "",
"offerSubType": "",
"compatibility": {},
"classification": [],
"images": {
"thumbanail": {
"imagePath": "http://s.tmocache.com/images/png/products/accessories/SUPM43270/SUPM43270-small.png"
}
},
"equipmentCharacteristics": {},
"offerVariants": {},
"type": "hard-good",
"offers": [],
"family": "IP6",
"pricePoints": {
"withServicePrice16GBNEW": {
"displayPriority": "1001",
"pricingMessage": "device price with service activation",
"price": "34.99",
"discounts": {}
}
},
"dynamicPricingData": {},
"inventoryData": {
"SKUGOLD16GBN": {
"availibility": "Pre-order now!",
"availableTimeline": ""
}
}
}
}
}
}
}
function find(obj, fieldName){
if(Array.isArray(obj)){
for(var i=0, len=obj.length;i<len;i++){
var nested = find(obj[i],fieldName);
if(nested.isFind) return nested;
}
}else{
if(typeof obj !== "object") return {isFind:false};
for(var i in obj){
if(i === fieldName) return {isFind:true, value:obj[i]};
var nested = find(obj[i],fieldName);
if(nested.isFind) return nested;
}
}
return {isFind:false};
}
var result = ['productName','shortDescription','imagePath','availibility'].map(function(el){ return find(obj,el).value});
document.getElementById('r').innerHTML = JSON.stringify(result,null,2);
<pre id='r'></pre>
Your json code is a complex data binding structure. It same like c# complex data binding. So you need to call the obj by through it call name.
for eg:
var data = {"ex":{"a":{"a1":"a1","a2":"a2"},"b":{"b1":"b1","b2":"b2"}}}
so data is a class and it includes "ex" object
data returns =>Object {ex: Object}
if you need to access "a" or "b" object means , you need to access through the"ex" object.
for eg:
data.ex.a => Object {a1: "a1", a2: "a2"}
in your code
for(var obj in acc.specifications){
accArray.push(obj);
}
obj only push 1st element of acc.sppectification object.
So please try this.
foreach(var obj acc.specification){
arr1.push(acc.specification[obj])
}
foreach (var obj acc.offers){
arr2.push(acc.offers[obj])
}

Categories