Javascript to access nested keys by category
Hello everyone,
Can anyone knowledgeable in Javascript show me how to access some nested dictionaries in a JSON file?
Initially the file had an unnamed array of subdictionaries with identical groups of keys (see attached), but dict wouldn't accept it so I enclosed it in a main key.
The JS code I have (below) gets me the name, key and subdictionary names (named 0, 1, 2... since they are actually unnamed). But when I try to go one step further into the subdictionary key-value pairs, I just get 'jsobject 0' in the Max console. Object.keys doesn't seem to work.
Also, what about querying values tied to a single key that recurs in all subdictionaries?
Appreciate any help!
function get_keys()
{
var dict = new Dict;
dict.import_json("fruits and peaches json3.json");
var keys = dict.getkeys();
var name = dict.name;
post("dict name: ");
post(name);
post();
post("key: ");
post(keys);
post();
var arr = dict.get(keys);
post("subkeys: ");
var arr2 = Object.keys(arr);
post(arr2);
var arr3 = dict.get(keys[1]);
post();
post(arr3);
}
Hi Demers,
Firstly, when getting the keys from a dictionary in JS, you should check if the keys you got are an array or a string.
If the dict has only one key, the dict.getkeys()
will return a string, if it has more it will return an array of strings.
What I do to prevent this is the following:
var dict = new Dict("my_dictionary_mame");
var keys = dict.getKeys();
// This will force the "keys" to be an array, even with a single element.
if (typeof(keys ) == "string")
keys = [keys ];
Other than that, when getting the subdict your line is var arr = dict.get(keys);
.
If the keys are an array, it will return you nothing.
You should do it like var arr = dict.get(keys[0]);
or any other index you want.
Now, to get the keys of a "subdict", you do it the same way:
var dict = new Dict("my_dict_bla_bla");
var keys = dict.getkeys();
if (typeof(keys ) == "string")
keys = [keys ];
// Here you get the subdictionary keys
var subdict = dict.get(keys[0]); // This is the first element of the topdict. Lets say it is a dictionary itself.
var subkeys = subdict.getkeys();
//etc...
Hope it helps a bit.
-N
Nikolas,
Really appreciate the help. I'm still running into trouble though; typeof identifies an object, but not whether it is an array or not. Oddly enough, even with the method you mentioned (converting a value subkey to [subkey]), Max says it isn't an array, but then generates a message saying it's not an object either!
Also, a command like: post(dict.fruitspeaches[0].business);
...doesn't yield anything, though I've tried this in a Javascript console, and it works.
The basic structure of the JSON file is below. The code is right after. Appreciate any clarification!
{
"fruitspeaches" : [
{
"business" : "Francis Motuzick Jr",
"category" : "Fruit",
"farmer_id" : "3402",
"item" : "Peaches",
"l" : "0",
"location_1" : {
"type" : "Point",
"coordinates" : [ -73.096275, 41.779892 ]
}
}
...
Here's the code:
var dict = new Dict;
dict.import_json("fruits and peaches 3.json");
var keys = dict.getkeys();
if(typeof(keys) == "string") // Force the 'keys' to be an array, even with single element
keys = [keys];
var name = dict.name;
post("dict name: ", name);
post("key: ", keys);
post("dict type: ", typeof(dict), "array? ", Array.isArray(dict)); // object, not an array
post("key type: ", typeof(keys), "array? ", Array.isArray(keys)); // object, is an array
post("keys[0] type: ", typeof(keys[0]), "array? ", Array.isArray(keys[0])); // says it's a string
post();
var subdict = new Dict(keys[0]);
if(typeof(subdict) == "string")
subdict = [subdict];
post("subdict type: ", typeof(subdict), "array? ", Array.isArray(subdict));
post();
var subkeys = subdict.getkeys();
post("subkeys type: ", typeof(subkeys), "array? ", Array.isArray(subkeys));
post();
post(Object.getOwnPropertyNames(subkeys));
post();
if(typeof(subkeys) == "string")
subkeys = [subkeys];
post(subkeys);
post();
Ok, I think there are some misconceptions that lead you to some errors in handlig the dictionaries in SJ.
You main proble is in this code:
var subdict = new Dict(keys[0]);
if(typeof(subdict) == "string")
subdict = [subdict];
.
the var subdict = new Dict(keys[0])
will create a new dict that will be named as the first key of the first Dict. That doesn't mean that it is the a sub Dict. The correct way to get the sub dict is by the get("keyName")
method of dict like so:
var subdict = dict.get(keys[0]);. This works because the
keys[0]` will return (in this case) the name of the 1st key, that happens to be another dictionary. To make sure that the dictionary element you get is another dict you should check that is it object but not array.
Secondly, here: if(typeof(subdict) == "string") subdict = [subdict];
you are comparing the dict element to see if it is a string, not its keys.
So it will return an array with on object inside, that is in this the sub dictionary.
So, to get the keys you do var mykeys = mydict.getkeys()
. This will return either an array of string, or a string (not ver cool but it is the way it is...). Only here you should do this check if(typeof(mykeys ) == "string") subdict = [mykeys ];
, and that is so you can easier write that handles the keys i.e. be sure that it is an array, and not have to check every time if its not.
To get a subdictionary you should do var thesubdict = mydict.get(keys[N]);
. Here you should make sure that it is indeed a dictionary - unless you are 100% sure that the Nth key is a subdictionary, bu still...
the var thesubdict
(or whatever name) you should treat as a dict (because it is a dict!), meaning the way you got the first dicts keys etc, the same way you will for the subdict, like var sudictkeys = thesubdict .getkeys()
etc...
I made the above corrections to your code and it works:
var dict = new Dict;
dict.import_json("fruits and peaches 3.json");
var keys = dict.getkeys();
if(typeof(keys) == "string") // Force the ‘keys’ to be an array, even with single element
keys = [keys];
var name = dict.name;
post("dict name: ", name);
post("\nkey: ", keys);
post("\ndict type: ", typeof(dict), "array? ", Array.isArray(dict)); // object, not an array
post("\nkey type: ", typeof(keys), "array? ", Array.isArray(keys)); // object, is an array
post("\nkeys[0] type: ", typeof(keys[0]), "array? ", Array.isArray(keys[0])); // says it’s a string
post();
post();
var subdict = dict.get(keys[0]);
post(subdict.stringify());
post("subdict type: ", typeof(subdict), "array? ", Array.isArray(subdict));
post();
var subkeys = subdict.getkeys();
post("subkeys type: ", typeof(subkeys), "array? ", Array.isArray(subkeys));
post(subkeys);
post();
post(Object.getOwnPropertyNames(subkeys));
post();
post();
if(typeof(subkeys) == "string")
subkeys = [subkeys];
post(subkeys);
post();
also the json you posted wasn't formatted correctly, so here it is
(also make sure that the json you use is correctly formated. To test it you can load it in an object [dict], if it is OK it will be read, else it will not) :
{
"fruitspeaches" : {
"business" : "Francis Motuzick Jr",
"category" : "Fruit",
"farmer_id" : "3402",
"item" : "Peaches",
"l" : "0",
"location_1" : {
"type" : "Point",
"coordinates" : [ -73.096275, 41.779892 ]
}
}
}
Using these two, it works.
-N
Nikolas,
Very much appreciate the explanation on how to invoke the subdict and proper formatting!
Strangely though, 2 problems came up with the corrected code you posted:
1. I get an error message that subdict.stringify is not a function.
2. The JSON formatting you did (with curly brackets throughout) wasn't accepted by my dict object, only the one with alternating { [ { was.
To be clear, the snippet of JSON I posted was only the beginning, a better representation would be this:
{
"fruitspeaches" : [
{
"business" : "Francis Motuzick Jr",
"category" : "Fruit",
"farmer_id" : "3402",
"item" : "Peaches",
"l" : "0",
"location_1" : {
"type" : "Point",
"coordinates" : [ -73.096275, 41.779892 ]
}
,
"location_1_city" : "Harwinton",
"location_1_location" : "16 Bogue Rd",
"location_1_state" : "CT",
"zipcode" : "06791"
}
, {
"business" : "Morning Song Farms",
"category" : "Fruit",
"farm_name" : "Morning Song Farms",
"farmer_id" : "16352",
"item" : "Peaches",
"l" : "18",
"location_1" : {
"type" : "Point",
"coordinates" : [ -73.224182, 41.787483 ]
}
,
"location_1_city" : "Litchfield",
"location_1_location" : "403 Beach Street",
"location_1_state" : "CT",
"phone1" : "860-361-6216",
"zipcode" : "06759"
}
]
}