searching through Json in JavaScript - javascript

I have a Json which have hierarchy of folders and files in it
I am trying to get number of file in a folder and its sub-folders by a folder Id
Here is the json
var jsonStr = {
"hierarchy": {
"date": "2014/09/24 15:21:23",
"folder": {
"name": "Root",
"id": "Root",
"file": [{
"id": "22U2621210__PIN_検査報告書Ver1.0_20140923162232.xls"
}, {
"id": "C22-1(EU仕様)_20140923162409.xlsx"
}, {
"id": "Machine_Inspection_20140923162329.xlsx"
}],
"folder": {
"name": "Level-1",
"id": "1411396172645",
"file": {
"id": "22U2621210__PIN_検査報告書Ver1.0_20140923162232.xls"
},
"folder": {
"name": "123",
"id": "1411538469568",
"file": [{
"id": "C22-1(EU仕様)_20140923162409.xlsx"
}, {
"id": "Machine_Inspection_20140923162329.xlsx"
}]
}
}
}
}
};
all the folders are with names and ids, if want to get number of files in that specific folder and its subfolders by searching with its id
for example if I put folder name "123" and id "1411538469568" it should give me only 2 files which are "C22-1(EU仕様)_20140923162409.xlsx" and "Machine_Inspection_20140923162329.xlsx" but if i put folder name "Root" and Id= "Root" it should return me id's of all files
Here is the Fiddle on which i am working on http://jsfiddle.net/ma3kno2o/

You can use Defiant.js
Here is a Fiddle for your concrete search scenario to pull the file IDs of the element with ID: root and Name: root:. I am using Defiant.js in this example:
http://jsfiddle.net/3z8mqr3u/1/
Defiant.js is superior to the custom search in the answer by #Cheery, to get the IDs of the files i had to use one line of code:
var ids = JSON.search(json, "//*[name = 'Root' and id = 'Root']/file/id");
it is much less error-prone for searching dynamic data. Defiant.js uses XPath expressions. Check this link to learn more:
http://defiantjs.com/
Here are some other options:
You can use plain JQuery
How to search JSON tree with jQuery
You can use JsonPath. It's like XPath for JSON files. You can do stuff like:
$..folder.file
http://goessner.net/articles/JsonPath/
https://code.google.com/p/jsonpath/
https://github.com/s3u/JSONPath
You can use Json-Query. It has it-s own language for deep queries. e.g.:
var data = {
grouped_people: {
'friends': [
{name: 'Steve', country: 'NZ'},
{name: 'Bob', country: 'US'}
],
'enemies': [
{name: 'Evil Steve', country: 'AU'}
]
}
}
jsonQuery('grouped_people[][country=NZ]', {data: data})
https://github.com/mmckegg/json-query
If you dont like any of these here you can find more options:
Is there a query language for JSON?

Not a nicest (sorry, 4am) solution, but the straight way through recursion..
Your structure does not support, in a normal way, same-level folders, so I reconfigured it, togerther with the code for it:
http://jsfiddle.net/ma3kno2o/5/
function getFiles(id)
{
var files = searchFolders(jsonStr.hierarchy.folders, false);
alert('Found ' + files.length + " files\n" + JSON.stringify(files));
function searchFolders(tree, count_files)
{
var data = [];
$.each(tree, function(key, val) {
var into = !count_files ? val.id == id : count_files;
if (val.files && into)
$.merge(data, getFiles(val.files));
if (val.folders)
$.merge(data, searchFolders(val.folders, into));
});
return data;
}
function getFiles(tree)
{
var files = [];
if (tree.id) return [tree.id];
$.each(tree, function(key,val) {
if (val.id)
files.push(val.id);
});
return files;
};
}
var jsonStr = {
"hierarchy": {
"date": "2014/09/24 15:21:23",
"folders": [{
"name": "Root",
"id": "Root",
"files": [{
"id": "file.1"
}, {
"id": "file.2"
}, {
"id": "file.3"
}],
"folders": [{
"name": "Level-1",
"id": "1411396172645",
"files": {
"id": "file.4"
},
"folders": [{
"name": "123",
"id": "1411538469568",
"files": [{
"id": "file.5"
}, {
"id": "file.6"
}]},
{
"name": "123",
"id": "1411538469569",
"files": [{
"id": "file.7"
}, {
"id": "file.8"
}]
}]
}]
}]
}
};
The old code will not work, so I rewrote it for your new varia
http://jsfiddle.net/ma3kno2o/8/
function getFiles(id)
{
var stp = -1;
var files = searchFolders(jsonStr.hierarchy, false);
alert('Found ' + files.length + " files\n" + JSON.stringify(files));
function searchFolders(tree, count_files)
{
var data = [];
var folders = tree.folder.length > 1 ? tree.folder : [tree.folder];
$.each(folders, function(key, val) {
var into = !count_files ? val.id == id : count_files;
if (val.file && into)
$.merge(data, getFiles(val.file));
if (val.folder)
$.merge(data, searchFolders(val, into));
});
return data;
}
function getFiles(tree)
{
var files = [];
if (tree.id) return [tree.id];
$.each(tree, function(key,val) {
if (val.id)
files.push(val.id);
});
return files;
};
}
var jsonStr= {"hierarchy":{"date":"2014/09/24 18:13:00","folder":{"name":"Root","id":"Root","file":[{"id":"file.1"},{"id":"file.2"},{"id":"file.3"}],"folder":[{"name":"Level-1","id":"1411396172645","file":{"id":"file.4"},"folder":{"name":"123","id":"1411538469568","file":[{"id":"file.5"},{"id":"file.6"}],"folder":{"name":"123-a","id":"1411549962260","file":{"id":"file.7"}}}},{"name":"level-2","id":"1411549976987","file":{"id":"file.8"}}]}}};

Related

How to increment values in an array json file based of from a name?

Example JSON file:
[
{
"discordId": "9273927302020",
"characters": [
{
"name": "Rare_Character",
"value": 1
},
{
"name": "Ultra_Rare_Character",
"value": 1
}
]
}
]
Let's just say for example I ran this simple gacha and got 4 characters:
let i = 1
var picks = []
while(i <= 4){
const { pick } = gacha.simple(alpha)
picks.push(pick)
i++
}
Now, picks has an array like this:
[
{
"name": "Common_Character"
},
{
"name": "Ultra_Rare_Character"
},
{
"name": "Common_Character"
},
{
"name": "Rare_Character"
}
]
How do I increment the value in My Example JSON file based on the name from what I got in my gacha results picks while ignoring the Common_Character and only passing those Rare and Ultra_Rare ones?
I've tried filtering them like this:
var filter = picks.filter(t => t.name === 'Rare_Character' || t.name === 'Ultra_Rare_Character')
Now I don't know how to increase those values in my JSON file and what if in the gacha results I got two Rare_Characters or Ultra_Rare_Character
I'm using fs to read my JSON file but I just don't know the logic to increase values
const src = [
{
"discordId": "9273927302020",
"characters": [
{
"name": "Rare_Character",
"value": 1
},
{
"name": "Ultra_Rare_Character",
"value": 1
}
]
}
];
const gacha = [
{
"name": "Common_Character"
},
{
"name": "Ultra_Rare_Character"
},
{
"name": "Common_Character"
},
{
"name": "Rare_Character"
}
];
const updateValues = (src, gacha) => {
const gachaSums = gacha.reduce((collector, current) => {
collector[current.name] = (collector[current.name] | 0) + 1;
return collector;
}, {});
src.characters.forEach(srcChar => {
gachaSums[srcChar.name] = srcChar.value + (gachaSums[srcChar.name] | 0);
});
src.characters = Object.entries(gachaSums).map(([key, value]) =>
({ name: key, value: value })
);
return src;
}
console.log(updateValues(src[0], gacha));
Maybe this version could help

Counting multiple json inputs js

I get an input like this:
input 1:
{
"name": "Ben",
"description": "Ben",
"attributes": [
{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
input 2
{
"name": "Ice",
"description": "Ice",
"attributes": [
{
"type": "Background",
"value": "Green"
},
{
"type": "Hair-color",
"value": "White"
}
]
}
input 3
{
"name": "Itay",
"description": "Itay",
"attributes": [
{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
What I want to do is count the amount of each type of background and each type of hair-color appearing.
(These are sample examples and in reality there are more types and different values)
Let's say in these examples we have 2 objects that have a background as default then I want to have a count of that like so:
export interface TraitCount {
value: string,
count: number
}
export interface CountOfEachAttribute {
trait_type: string,
trait_count: traitCount[] | null,
total_variations: number
}
I want the most effective code because there are other aspects to the code, in addition it will run on 5-10k queries not just three, so needs
to run in good times too :D
(It's similar to my other question done with python but now I need it in js also)
Atm it's something like this:
(Apart of a much bigger code so keep that in mind)
setInitalCountOfAllAttribute( state, { payload }: PayloadAction<CountOfEachAttribute[] | null> ) {
if (payload === null) {
state.countOfAllAttribute = null;
} else {
state.countOfAllAttribute = payload;
}
},
setCountOfAllAttribute(state, { payload }: PayloadAction<Attribute>) {
if (state.countOfAllAttribute !== null) {
state.countOfAllAttribute.map(
(countOfEachAttribute: CountOfEachAttribute) => {
// Find the trait type
if (countOfEachAttribute.trait_type === payload.trait_type) {
// initiate the trait count array to store all the trait values and add first trait value
if (countOfEachAttribute.trait_count === null) {
const new_trait_count = { value: payload.value, count: 1 };
countOfEachAttribute.trait_count = [new_trait_count];
countOfEachAttribute.total_variations++;
}
// Trait array already existed.
else {
// Check if value already present or not
const checkValue = (obj: any) => obj.value === String(payload.value);
const isPresent = countOfEachAttribute.trait_count.some(checkValue)
const isPresent2 = countOfEachAttribute.trait_count.find((elem: any) => elem.value === String(payload.value))
// Value matched, increase its count by one
if (isPresent2) {
countOfEachAttribute.trait_count &&
countOfEachAttribute.trait_count.map((trait) => {
if (trait.value === payload.value) {
trait.count++;
}
});
}
// Value doesn't match, add a new entry and increase the count of variations by one
else {
const new_trait_count = { value: payload.value, count: 1 };
countOfEachAttribute.trait_count = [
...countOfEachAttribute.trait_count,
new_trait_count,
];
countOfEachAttribute.total_variations++;
}
}
}
}
);
}
},
You can merge all arrays and use Array.reduce.
const input1 = {
"name": "Ben",
"description": "Ben",
"attributes": [{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
const input2 = {
"name": "Ice",
"description": "Ice",
"attributes": [{
"type": "Background",
"value": "Green"
},
{
"type": "Hair-color",
"value": "White"
}
]
}
const input3 = {
"name": "Itay",
"description": "Itay",
"attributes": [{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
const mergedInput = [input1, input2, input3];
const result = mergedInput.reduce((acc, item) => {
item.attributes.forEach(attrItem => {
const existType = acc.find(e => e.trait_type == attrItem.type);
if (existType) {
var existAttr = existType.trait_count.find(e => e.value == attrItem.value);
if (existAttr) {
existAttr.count++;
} else {
existType.trait_count.push({
value: attrItem.value,
count: 1
});
existType.total_variations++;
}
} else {
acc.push({
trait_type: attrItem.type,
trait_count: [{
value: attrItem.value,
count: 1
}],
total_variations: 1
})
}
});
return acc;
}, []);
console.log(result);
I suggest instead of creating an array for trait_count to make it an object so you don't have to iterate over it whenever you are adding a new attribute. In the snippet below I'm using the value of the attribute as a sort of hash that allows the access to the given property without having to call the Array.prototype.find function
const input1 = {"name":"Ben","description":"Ben","attributes":[{"type":"Background","value":"Default"},{"type":"Hair-color","value":"Brown"}]};
const input2 = {"name":"Ice","description":"Ice","attributes":[{"type":"Background","value":"Green"},{"type":"Hair-color","value":"White"}]};
const input3 = {"name":"Itay","description":"Itay","attributes":[{"type":"Background","value":"Default"},{"type":"Hair-color","value":"Brown"}]};
function countAtributes(input, totalCounts={}) {
input.attributes.forEach((attribute) => {
if (!totalCounts[attribute.type])
totalCounts[attribute.type] = {trait_type: attribute.type, trait_count: {}, total_variations: 0};
if (!totalCounts[attribute.type].trait_count[attribute.value]) {
totalCounts[attribute.type].trait_count[attribute.value] = {value: attribute.value, count: 1};
totalCounts[attribute.type].total_variations+=1;
}
else totalCounts[attribute.type].trait_count[attribute.value].count +=1;
})
}
const totalCounts = {};
countAtributes(input1, totalCounts);
countAtributes(input2, totalCounts);
countAtributes(input3, totalCounts);
console.log(totalCounts);
It could be turned into the array afterwards with Object.values if necessary
I believe it is a much better approach to what you had before as you don't have to iterate over the tables of trait_counts. In theory it should significantly reduce the time taken. Iterating over the array and checking a condition each time is much slower than key lookup in Javascript object

Javascript changing multiple object values

I am trying to replace / change the values in an object, but I can't seem to work out how it's done or if it can even be done.
I'm trying to add https://SiteName.com to the start of each of the values so it will be like https://SiteName.com\/asset\/image\/map\/map-grass.svg
var assets = [{
"name": "\/asset\/image\/map\/map-grass.svg",
"url": "\/asset\/image\/map\/map-grass.svg"
}, {
"name": "\/asset\/image\/map\/map-stone.svg",
"url": "\/asset\/image\/map\/map-stone.svg"
}]
Object.keys(assets).forEach(key => {
const val = assets[key];
console.log(val)
});
Try this:
var assets = [{
"name": "\/asset\/image\/map\/map-grass.svg",
"url": "\/asset\/image\/map\/map-grass.svg"
}, {
"name": "\/asset\/image\/map\/map-stone.svg",
"url": "\/asset\/image\/map\/map-stone.svg"
}]
let url = "https://SiteName.com";
Object.keys(assets).forEach(key => {
const val = assets[key];
val.name = url + val.name;
val.url = url + val.url;
});
console.log(assets)
You need a nested loop (or forEach) here - one to go over the elements of the assets array, and then, for each object in there, go over all its properties:
var assets = [{
"name": "\/asset\/image\/map\/map-grass.svg",
"url": "\/asset\/image\/map\/map-grass.svg"
}, {
"name": "\/asset\/image\/map\/map-stone.svg",
"url": "\/asset\/image\/map\/map-stone.svg"
}]
assets.forEach(o => {
Object.keys(o).forEach(key => {
o[key] = 'https://SiteName.com' + o[key];
})
});
console.log(assets);

Postman JSON parse response body arrays inside arrays

I have this JSON Response from API call
[
{
"id": 20599,
"name": "Deliver",
"options": [
{
"id": 63775,
"name": "Item",
"dataType": "SelectMultiOption",
"required": false,
"options": [
{
"id": 426,
"name": "Towels"
},
{
"id": 427,
"name": "Toothbrush"
},
{
"id": 428,
"name": "Pillow"
}
]
}
]
}
]
I am using this code to get the id of the service "Deliver"
var data = JSON.parse(responseBody);
var loop_count = 0
for (count = 0; count < data.length; count++)
{
if (data[count].name == "Deliver")
{
var job_id = data[count].id;
postman.setEnvironmentVariable("service_id", job_id);
}
}
The questions are:
How can I get value from array "options", I need to get the "id":
63775 and store as "item_id" and the "name":"Item" as "item_name" postman variables.
Then I need to select the "options" nested in record
"Item" and select the option "name": "Toothbrush" and store in postman
variable "svc_optn_optn_name" and it's "id" stored in
"svc_optn_optn_id"
Here I am giving my own suggestion for your problem with few lines of code. I am not sure, how are you going to use these values. I also don't know if the outer options array will always have 1 item or more. I have just tried to satisfy your questions.
Please ask/comment, if you have more doubts or I am wrong.
I have created a function getAllPostmanDataFrom(obj) which takes object as parameter which is the value of data[count], gathers necessary info in other object postmanObj and returns it to the caller.
function getAllPostmanDataFrom(obj) {
const item_id = obj.options[0].id;
const item_name = obj.options[0].name;
const svc_optn_optn_name = obj.options[0].options[1].name;
const svc_optn_optn_id = obj.options[0].options[1].id;
const postmanObj = {item_id, item_name, svc_optn_optn_id, svc_optn_optn_name}; // Return object
return postmanObj;
}
var data = [
{
"id": 20599,
"name": "Deliver",
"options": [
{
"id": 63775,
"name": "Item",
"dataType": "SelectMultiOption",
"required": false,
"options": [
{
"id": 426,
"name": "Towels"
},
{
"id": 427,
"name": "Toothbrush"
},
{
"id": 428,
"name": "Pillow"
}
]
}
]
}
]
var count = 0;
var obj = data[count];
var postmanObj = getAllPostmanDataFrom(obj);
//var {item_id, item_name, svc_optn_optn_id} = postmanObj;
console. log(postmanObj)
/*
console.log(item_id);
console.log(item_name);
console.log(svc_optn_optn_id);
console.log(svc_optn_optn_name);
*/
Finally, you can use values contained in postmanObj as follows:.
postman.setEnvironmentVariable("item_id", postmanObj.item_id);
postman.setEnvironmentVariable("item_name", postmanObj.item_name);
And so on.
This is the solution
var data = JSON.parse(responseBody);
variable named as data
var loop_count = 0
for (count = 0; count < data.length; count++)
{
if (data[count].name == "Deliver")
{
var job_id = data[count].id;
postman.setEnvironmentVariable("service_id", job_id);
var job1_name = data[count].options[0].name;
postman.setEnvironmentVariable("item_name", job1_name);
var job2_id = data[count].options[0].id;
postman.setEnvironmentVariable("item_id", job2_id);
var job3_id = data[count].options[0].options[1].id;
postman.setEnvironmentVariable("svc_optn_optn_id", job3_id);
var job4_name = data[count].options[0].options[1].name;
postman.setEnvironmentVariable("svc_optn_optn_name", job4_name);
}
const data = JSON.parse(responseBody);
data.forEach(item => {
console.log(item.id); // deliver object id.
item.options.forEach(option => {
console.log(`Option Id ${option.id}`); // option id
postman.setEnvironmentVariable("service_id", option.id);
option.options(optionItem => {
if(optionItem.name == 'Toothbrush'){
postman.setEnvironmentVariable("svc_optn_optn_name", optionItem.name);
postman.setEnvironmentVariable("svc_optn_optn_id", optionItem.id);
}
});
});
});

How to retrive property values from array of objects in javascript

[{
"circlemarker": [{
"type": "circle_marker"
}, {
"latlong": "abc"
}]
}, {
"connector_marker": [{
"type": "icon_marker"
}, {
"latlong": "pqr"
}]
}, {
"icon_marker": [{
"type": "connector_marker"
}, {
"latlong": "xyz"
}]
}]
I want to access latlong values of each marker. So how can I have access to each property in this structure.
You can get latlong data:
for (var a = 0; a < obj.length; a++) {
var key = Object.keys(obj[a])[0];
var latlong = obj[a][key][1];
console.log(latlong));
}
But i think that data have not correct structure, this is better solution:
var markers = [{
"name": "circlemarker",
"type": "circle_marker"
"latlong": "abc"
}, {
"name": "connector_marker",
"type": "icon_marker",
"latlong": "pqr"
}, {
"name": "icon_marker",
"type": "connector_marker",
"latlong": "xyz"
}];
I think this should work for you:-
var makers = [{"circlemarker":[{"type":"circle_marker"},{"latlong":"abc"}]},{"connector_marker":[{"type":"icon_marker"},{"latlong":"pqr"}]},{"icon_marker":[{"type":"connector_marker"},{"latlong":"xyz"}]}];
makers.forEach(function(maker){
var makerName = Object.keys(maker)[0];
console.log(maker[makerName][1]["latlong"]);
});
so for each object in the array, you want to pluck the latlong from the first key which also references another array of objects. Man I would fix this data structure but if you can't control it, you can do this:
#!/usr/bin/env node
var data = [{
"circlemarker": [{
"type": "circle_marker"
}, {
"latlong": "abc"
}]
}, {
"connector_marker": [{
"type": "icon_marker"
}, {
"latlong": "pqr"
}]
}, {
"icon_marker": [{
"type": "connector_marker"
}, {
"latlong": "xyz"
}]
}];
var _ = require('lodash')
, coords = [];
_.each(data, function(item){
//console.log(item);
var key = _(Object.keys(item)).first()
, first = item[key]
, latLong = _.pluck(first, 'latlong')[1];
if ( latLong ) {
coords.push(latLong);
}
});
console.log(coords);
Produces the following output:
[ 'abc', 'pqr', 'xyz' ]

Categories