JavaScript and Dict
Hi there
I am wondering if anyone can give me a simple example of how I can get data from a dictionary into a Js object to work with it
Lets say a function that takes each key and posts its data to the max "terminal"
Thanks a lot :)
Hi again
I am wondering where the 'get' in: d.get(keys[i]
... is coming from ?
I am trying to find a way to throw the whole value list of the d object out of the 0 output of the max js object , but I only get one set of numbers (the last one, out of 200)
var d = new Dict("funkyDict");
function post_to_max(){
var keys = d.getkeys();
for(var i = 0; i < keys.length; i++){
post(d.get(keys[i]), "\n"); // This will post the value
outlet(0, d.get(keys[i])); // This will post the value
}
}
This is all very nice, get and set. But is there any way to make max Dicts into first class objects in javascript.
If you are aware of json use in web/javascript. There is no need for gets and setters, because JSON is the syntax of javascript objects.
so
var me = {"username" : "furiousgreenloud", age : 41"};
means you can use your me.username, DIRECTLY in your javascript, plus all the iteration and the rest of the language is just there!
I would expect a new Dict("me") to return an actual javascript associative array (which is an object), or as least, there to be a new Dict("me").obj() method. Or at very least new Dict.borrow("me") and then new Dict.return("me") methods?!!
much love & respect.
Hi Furious, did you come across my wiki post?
Hi Furious, did you come across my wiki post?
This is exactly what I want to do, however I still don't understand how to convert a javascript object to a dict.
Well, the jobj_to_dict() method does that, but what I don't understand is how to output the dict object from the js object.
This is what I want to do:
var jsdata = dict_to_jsobj(inputDict);
var myNewDict = jsobj_to_dict(jsdata);
outlet(0,myNewDict);
This obviously doesn't work - because the outlet is actually a jsobj with a reference to the dict object.
So how do I actually send a dict to an output? Or is this not possible? I have created this wonderful new dict object (myNewDict) - how do I now use this in my patcher?
I think it depends whether you are using max 7 or max 6.
I haven't started using max 7 yet, but I believe you can output a dict object from a js object somehow now. In max 6, you can only output a stringified version of the object like so:
function bang()
{
output=JSON.stringify(myDictObject); //also the args: , null, "\t"
outlet (0, output);
}
I think it depends whether you are using max 7 or max 6.
I haven’t started using max 7 yet, but I believe you can output a dict object from a js object somehow now. In max 6, you can only output a stringified version of the object like so:
Interesting... I haven't tried this yet, but if you output a dict as string, then it behaves as a dict object to other Max objects?
I am using Max 7, and it is not obvious that there is a way to output a dict from js through an outlet, thought from what I have gathered through reading the forum, the Max 7 documentation has not caught up yet to the actual product.
So far, the only way I have been successful at exporting to a dict is creating a "named" dict object in js - where the named dict object is a dict that is also in my patcher. I don't like this way, but it is working.
thanks mattijs, your example works in max 6 of course; i was thinking of nested dictionaries not working in max 6, but now possibly working max 7....
so the crux of the problem for johnny was formatting the output correctly:
var newDict = jsobj_to_dict(data);
outlet(0, "dictionary", newDict.name);
Here is a project with very simple nested dictionaries - and MATTIJS's example of dict_to_jsobj seems to be able to parse this just fine. I also tried parsing the json by reading in the json file and parsing it using JSON.parse() - which has been part of the javascript library since like 2009 or so...
As outlined in this post here:
The JSON.parse() method works when running inside of Max but not when the project is exported as a Max Application... weird...
This is all with Max 7 on Mac OS 10.10.1
I think I'm about to give up on Max 7 for a while... The project included ^^^^^^ is now, no longer working for me.
dict_to_jsobj() was working just fine... and now, it can not handle nested dicts... This is so crazy. It was working and then it just stopped.
I think I might have figured out what is going on... When you use a dict - it seems as though Max saves the contents of this dict to a file based on the name of the dict (and that name is not patch specific). Here is the weird thing. If you close, say patch 1 that uses a dict with a file named dict1 - and open a new patch that also uses a dict named dict1, it will load patch 1's file, even if the parsing of dict1 in this new patch failed.
So it seems, what I had perceived as Max 7 successfully parsing nested dicts, was indeed Max picking up a previously cached version of a dict with the same name, that had been parsed by JSON.parse() method, and not the nested dict_to_jsobj() method.
I guess the good thing is that I am learning how NOT to do things in Max (like use javascript and json) - and just hardcode everything in static tables. Ugh.
Nested dicts work fine, but the problem you are experiencing is that Max (6 or 7) doesn't support arrays of dicts.
Unfortunately this is the only missing link, otherwise dict would be a complete way to interface with javascript objects / JSON.
If you would re-format your json file like so, everything should work:
{
"colorTable":{
"0" : {
"ColorName": "off",
"Color": "",
"HexColor": "000000",
"Red": "0.0",
"Green": "0.0",
"Blue": "0.0"
},
"1": {
"ColorName": "light-white",
"Color": "",
"HexColor": "FFFFFF",
"Red": "1.0000",
"Green": "1.0000",
"Blue": "1.0000"
},
etc...
}
}
Nested dicts work fine, but the problem you are experiencing is that Max (6 or 7) doesn’t support arrays of dicts.
That's a fairly significant limitation (IMO), but thanks for the heads up. The weird thing is that if you import the json file with arrays of dicts into a dict and use dict.view to display the file, it displays just fine.
If you would re-format your json file like so, everything should work:
That would work, and so would doing the parsing and flattening of the array of dicts using and external process, like node.js - but that introduces a manual step to the process. The original .json file is not hand-made, it is exported from a spreadsheet.
I agree that it is a big limitation. But I didn't know that dict.view does support arrays now. That bodes well for the future :) I do think that the issue is on Cycling's radar.
At the moment, getting to the content of the sub-dicts with Max objects doesn't seem to be possible, see my quick attempt (needs to be appended to your example patch):
Not sure what your final goal is, but if it is to output single colors from the colorTable in your example, it should be possible to write a recursive method that converts the JSON-parsed jsobj containing the array into a nested dict that max understands like in my previous post. No need for node.js I'd say?
JSON-parsed jsobj
It is totally possible with JSON.parse() if that is what you mean. But one problem with that is that for some reason JSON.parse() is not recognized when a patch is exported as an application. It is almost like the js runtime that Max uses when running patches internally in Max is different than the runtime that they use when exporting a patch as an Application. Or maybe it's just a bug.
I think JSON.parse() is the route I am going to take for now, and just add shim for JSON.parse() if it is not detected.
have a look at the last two posts in this thread:
https://cycling74.com/forums/javascript-dict-and-an-array-of-dicts-as-a-dict/
i posted a solution to a similar problem i had sometime earlier-- (sorry, forgot i did otherwise would have directed it to you earlier). you might find it useful to adapt to your situation...
edit: actually upon reading your latest post it's not going to help you; misunderstood your issue
@MATTIJS Max 7 support arrays of dictionary. Check out the arrays tab in dict's helpfile.
It does!!! I can't believe I missed this one! Thanks so much guys, my prayers have been answered!
Ok everyone, please forget everything I said about not using arrays.
@EMMANUEL: do you happen to know if the new gettype and getsize messages have a javascript equivalent? If so, I can adjust the dict_to_jsobj and inverse scripts to support arrays and that should fix @LIGHTSPEED.JOHNNY's problem.
Not currently. I'll add a feature request for them.
Great!
@MATTIJS
i dont know if that would help , but if u are ok to retrieve "key" in order to check its "legacy" you can ask
(yourKey instandeof Dict)
which will say "true" if it as a dictionary
@MATTIJS, sorry there is still no PM here...
After I wrote a small JS-Object-Printer (Max 7 module style) for my own debugging purposes I realized that you posted something similar in the WIKI. This one is aiming a bit more generally to print properties of JS objects (not only JSON objs), it takes case of the JavaScript oddity that some data types can be objects or primitives (but still behaving as object) and therefore will return different values when using typeof.
It also detects Max specific objects (such as Task, Maxobj etc.) and I build in a protection for endless iterations that might occur with some of these objects (i.e. when passing a reference to jsthis or patcher).
I wanted to add/comment this to your original post. Unfortunately only the author seems to be able to edit it...
I refactored your code (plus my printer function) into a Max 7 module called "dictTools" for easier use with require(). Here it is, just just in case you might like to add this to your WIKI post.
Cheers,
Jan
PS: I did not test it but i saw that in the jsobj_to_dict you use typeof. That might create trouble, I believe:
if (!(typeof value === "string" || typeof value === "number")) {
...
}
If String, Number or Booleans are created with the constructor (var bla = new String("foobar")) valueof will return Object and bla.constructor.name will be "String"....
I just tested it: jsobj_to_dict(jsObj)
crashes Max when jsObj contains a non primitive data type.
this should fix it:
// convert Objects to primitive data types, Boolean to int
if(typeof value === "object") {
switch (value.constructor.name) {
case "String" :
value = value.toString();
break;
case "Number" :
value = value.valueOf();
break;
case "Boolean" :
value = (value) ? 1 : 0;
break;
}
}
// convert primitive boolean to int
if(typeof value === "boolean") {
value = (value) ? 1 : 0;
}
if (!(typeof value === "string" || typeof value === "number")) {
...
}
@DO…WHILE : I observed that (yourKey instandeof Dict)
only seems to work for dicts inside dicts but not for arrays of dicts. The array keys in such a case are numeric (as expected) but the related values are not instances of Dict but strangely enough of Number. I'd say it's a bug / incomplete JS implementation...
Maybe someone can proof me wrong ;)
Cheers
Hi Jan, Great work!
It seems weird that you can't edit the wiki page I made. I can edit the pages of others as long as I am logged in.
It would be ideal if you could edit the wiki page yourself as you see fit, but if you can't I can change it to include your modifications in the following days.
Hi Mattijs,
indeed I can only edit pages that I created myself... that's not the idea of a WIKI, isn't it :) I'll check with support/Lilli.
Ok - found something else I can't figure out how to do with the dict object... Arrays of arrays - is this possible using the dict methods?
Here is a simple example:
var myDict = new Dict("myDict");
myDict.parse('{ "points":[]}');
// ok now myDict has a single key called "points" which is an empty array - now lets fill it with some arrays of ints...
// this is how you do it in js
var obj = {};
obj.points = [];
for (var i = 0; i < numpoints; i++)
{
// assume we get data in vars x,y,rgb
obj.points.push([x,y,rgb]);
}
// now we have something (obj) that looks like this:
// { "points: [ [0, 0, 0], [1, 1, 1]...] }
// how the heck to you do this with myDict without doing this hack:
var hack = JSON.stringify(obj);
myDict.parse(hack);
The object->JSON.stringify->dict hack wouldn't be that bad, but I still can't get JSON.stringify to work when I export the patch as an application... ugh. Also, I can't imagine that it is super efficient when the number of points gets large - like say 5000.
Hi all,
Thank you very much for this.
But I have a small issue with it. If I use a numbers as key in the Dict, then it always crashes.
********************************
This crashes:
{
“CC names” : {
”0” : “Bank Select MSB”,
”1” : “Modulation”,
”2” : “Breath Controller”
}
}
********************************
This doesn’t crash:
{
“CC names” : {
”CC0” : “Bank Select MSB”,
”CC1” : “Modulation”,
”CC2” : “Breath Controller”
}
}
********************************
Do you have any hint or solution for me, please?
Thanks!
For the archives, much simpler ways to convert a js object from and to dict (now?) exist:
function dictionary(dictName)
{
var inputDict = new Dict(dictName)
var jsObject = JSON.parse(inputDict.stringify())
}
function bang()
{
var outputDict = new Dict()
outputDict.parse(JSON.stringify(jsObject))
}
Hi Mattijs,
I tested this and it does not work or maybe I miss something? (MAX 8.10 on Mojave 10.14.6 (Intell))
With your code in the java-script object.
Hi @HANS LEEUW,
If you use the JS exactly, how MATTIJS KNEPPERS wrote it, the "jsObject" variable is not defined in the bang function.
You can define it as a global variable, for example like this:
var jsObject = {}
function dictionary(dictName)
{
var inputDict = new Dict(dictName)
jsObject = JSON.parse(inputDict.stringify())
}
function bang()
{
var outputDict = new Dict()
outputDict.parse(JSON.stringify(jsObject))
}
Btw, myself, I handle the dict from the JS only, once I instantiate it in the JS. It's much easier to handle it in the code for me.
Hi Hans,
As Martin says, if you want to literally convert a dict to Javascript and back, you need access to the parsed Javascript object in the bang method. This was not clear in my example, sorry.
Also, if you want to output the resulting dict, you also need to send it to an outlet. For the archives, below I included your patch and the adapted script to make it a complete working example.
save as testDictionary.js:
var jsObject = {}
function dictionary(dictName)
{
var inputDict = new Dict(dictName)
jsObject = JSON.parse(inputDict.stringify())
post(jsObject.aList) // print a member of the object to the Max window
}
function bang()
{
var outputDict = new Dict()
outputDict.parse(JSON.stringify(jsObject))
outlet(0, "dictionary", outputDict.name)
}
Ah, great. Thanks for the quick reactions. My JS skills are rusty. I am looking for ways to transfer some of my FTM work to other devices and JS seems good for some of it. Knowing it is fairly easy to parse dictionaries back and forth was one of the things to investigate before diving further into it.
Thanks for the good work.