javascript, dict, and an array of dicts as a dict


    Aug 12 2014 | 8:29 am
    I've been trying to create an array of dicts using javascript, but as you can see from the patch it's not working. If some nice person could look at this patch, and place the accompanying js code in a file called subDictAdder.js, you may be able to show me what I'm doing wrong. If you look at my previous post: https://cycling74.com/forums/is-an-array-of-dicts-possible/ you'll have a better understanding of what i'm trying to do.
    Thanks!
    var subDictArray= new Array();
    function add(subDictName) { subDict= new Dict(subDictName); subDictArray.push(subDict); }
    function bang() { outlet (0, jsobj_to_dict(subDictArray)); }
    function clear() //purge the array of sub-dicts { while (subDictArray.length) { subDictArray.pop(); } }
    //this function is from https://cycling74.com/wiki/index.php?title=Parsing_Dict_files_to_Javascript_objects function jsobj_to_dict(o) { var d = new Dict();
    for (var keyIndex in o) { var value = o[keyIndex];
    if (!(typeof value === "string" || typeof value === "number")) { var isEmpty = true; for (var anything in value) { isEmpty = false; break; }
    if (isEmpty) { value = new Dict(); } else { var isArray = true; for (var valueKeyIndex in value) { if (isNaN(parseInt(valueKeyIndex))) { isArray = false; break; } }
    if (!isArray) { value = jsobj_to_dict(value); } } } d.set(keyIndex, value); } return d; }`

    • Aug 12 2014 | 9:44 am
      Hi i dont have Max here , and its hard to read you post formatting . i will try to point somewhere . note that you are passing jsobj_to_dict(subDictArray) an array to the function instead of Dict itself (the function iterates on an Object type object assuming that it has object structure with KEYS). However if u want to nest Dict's inside a Dict , u will just need to assign sub Dict's to a Keys in your master Dict and return itself after all !
    • Aug 12 2014 | 12:30 pm
      Thanks for your response (and the other one in the other thread). It has helped to clarify things a little.
      But, I want to generate this _valid_ structure:
      {
           key : [object, object, object....]
      }
      
      with each object being a dict like this
      {
           key : val, key : val, key : val
      }
      
      in other words, I want an array of objects which are accessible by one key. Why? because each object in the array have corresponding keys that are identical (namely "title", "start_time", "duration"), so I need to put them in an array.
      Does that make sense?
      I have modified my js so it _almost_ works. Hopefully you have some time to look at the patch, which I've modified a little:
      and here is the modified js that goes in [js subDictAdder]
      var subDictArray= new Array();
      var  i=0;
      
      function add(sourceDictName)
      {
      	i++;
      	var subDictName=sourceDictName+'_'+i;
      	sourceDict= new Dict(sourceDictName);
      	subDict = new Dict(subDictName);
      	subDict=sourceDict;		//is this how to clone? Don't think so; 
      //is there a clone method?
      	subDictArray.push(jsobj_to_dict(subDict));//EDIT typo fixed
      	post ("\nadded " +subDictName+" "+subDict.toString());
      }
      	
      function bang()
      {
      	outputDict= new Dict();
      	outputDict.set("playlist", jsobj_to_dict(subDictArray));
      	outlet (0, "dictionary "+outputDict.name);	
      }
      
      function clear()		//purge the array of sub-dicts
      {
      	while (subDictArray.length) { subDictArray.pop(); }
      	i=0;
      }
      
      function jsobj_to_dict(o) {
      	var d = new Dict();
      	
      	for (var keyIndex in o)	{
      		var value = o[keyIndex];
      
      		if (!(typeof value === "string" || typeof value === "number")) {
      			var isEmpty = true;
      			for (var anything in value) {
      				isEmpty = false;
      				break;
      			}
      			
      			if (isEmpty) {
      				value = new Dict();
      			}
      			else {
      				var isArray = true;
      				for (var valueKeyIndex in value) {
      					if (isNaN(parseInt(valueKeyIndex))) {
      						isArray = false;
      						break;
      					}
      				}
      			
      				if (!isArray) {
      					value = jsobj_to_dict(value);
      				}
      			}
      		}
      		d.set(keyIndex, value);
      	}
      	return d;
      }
      EDIT: sorry code refuses to format in browser
    • Aug 12 2014 | 2:54 pm
      Hi . i think i understand . u want to nest Dict's in a master Dict . but at the same time to hold them in an array in order to iterate through for key retrieval ? yes thats possible but perhaps u will want to nest dicts as a reference instead of cloning , so perhaps u dont need to clone anything .
      var subDictArray = new Array();
      var sourceDictName = "MainDict" ;
      var i = 0;
      
      function add(sourceDictName) {
          i++;
          var subDictName = sourceDictName + '_' + i;
          var subDict = new Dict(subDictName);
      
          var sourceDict = Dict(sourceDictName);
      
          subDictArray.push(subDict);
          sourceDict.replace(subDictName,subdict) ;
      }
      
      i hope it will work as im not able to test it . let me know so i can investigate later on .
      #
      for the note : if u will ever want to clone Dict in JS u can use clone function that expects name of a dict to clone :
        var newDictionary = new Dict();
        newDictionary.clone(oldDictionary.name);
      
      # EDIT but u could do it all without an Array , directly on Dict . a bit of pita but possible . will show u later
      # EDIT 2 use "pre" tags to format your code . the forum is broken .i think Miss Ginger ignored my report
    • Aug 12 2014 | 3:29 pm
      i think array of Dicts in a Dict should also work . try this .
      var subDictArray = new Array();
      var sourceDictName = "MainDict" ;
      var sourceDict = new Dict(sourceDictName);
      sourceDict.set("playlist","");
      
      var i = 0;
      
      function add(sourceDictName) {
      
          var subDictName = sourceDictName + '_' + i;
          var subDict = new Dict(subDictName);
      
          var sourceDict = Dict(sourceDictName);
      
          subDictArray.push(subDict);
      
          sourceDict.replace("playlist["+i+"]",subdict) ;
          i++;    
      }
      
      
      # EDIT
      now as u know your main Dict instance name , you can refer to it in any place in Max and use it as it is .
    • Aug 13 2014 | 12:31 am
      Hey dowhile, thanks for your thoughts and help, I wouldn't have been able to get this far without them
      i think i understand . u want to nest Dict’s in a master Dict . but at the same time to hold them in an array in order to iterate through for key retrieval ?
      not quite; all I am trying to do is conform to a client's json template; once it is formatted correctly all I will do is serialize it and send via udp (to a mobile phone app server). So I don't need to access it within max at all via the dict system (although if i get it right it could be useful in other parts of the patch)
      So I feel either I am very close or it is not going to work at all. Unfortunately your 2nd example doesn't work.
      It works for the first item, playlist[0], but if i try to add another I get: dictwrap: index out of range for key playlist[1] in the max window.
      I thought maybe playlist should be an array, so I tried declaring playlist as an Array: var playlist = new Array(); but this caused a compilation error: freeobject: 122c0da8: bad object
      So it seems to me Max wont allow arrays of objects in its dict system. if this is so then it's a severe limitation on the flexibility that having json structutres in max would otherwise allow. Maybe I'm missing something?
    • Aug 13 2014 | 8:40 am
      Hi ! im glad that at least i could point you somewhere . Yes , it was crazy assumption of mine that array of Dict's would be possible . Now i can make sure that it wont work . sorry i could not test it yesterday . Just one question . is your shared json a single playlist entry ? that is concentrated at the host ? i would want to index them for easier maintaining . u will be able to decide about any of the entries separately . is your design not allowing for key data access somehow ,so u had to decide to use an array index type of access?
    • Aug 13 2014 | 11:11 am
      it was crazy assumption of mine that array of Dict’s would be possible
      not crazy, I assumed so as well ;-] I'm glad you tried it out for yourself too -- it's actually the first time I've used javascript, so I thought oh maybe it's just me.
      There will be more than one playlist; and there is other information transmitted too. My postings in this thread are to do with a subset of a larger data-set that will be used. And they are concentrated at the host (server). At the moment we (me and someone else) are designing a protocol for the app, so I guess I'll go back to him and say do we really need arrays-- I'm not sure how he wants to access the data, but I assume it would be to do with array indexing (as it's a playlist, so hey it's ordinal data after all..)
      But thanks, your expertise and willingness is very much appreciated.
      I have to say this could have been cleared up by someone at c74 headquarters very easily a couple of days ago. It seems the dict system is kind of half thought through and not particularly flexible. It's actually wasted my time in a way, because I could have built a coll-to-json converter myself in java (or do some weird max patch) by now rather than having to negotiate the lack of documentation and rely on the good will of the forum... ho hum
    • Aug 13 2014 | 3:35 pm
      Hi guys,
      I don't work for C74 but - as a Max/MSP user - I can tell you that the dict system is very well thought out and very flexible. And if you had done a more extensive research you would have come to the same conclusion as well.
      An array of dictionaries exactly as Floating Point described is definitely possible and it's very easy to implement.
      The solution is in C but the same principles may apply to other languages as well.
      I don't use Javascript and I don't know if it is possible to do in Javascript. But remain assured that if it's not possible that is because of a limitation of the Javascript language not the dict structure or API.
      - Luigi
    • Aug 13 2014 | 4:33 pm
      Hello Guys !!@FP this array thing was really bothering me as i know the thread of Luigi already , i thought it would be as it is with setting or appending stack of dicts . unfortunately i even experienced crashing . im just seeing "dictwrap" value and thats not always true anyway . its dangerous . I think u could design different pattern for this with nesting Dict's as a keys then (a standard way). U could just maintain it after receiving data from outside . I could even help to design it if u will stuck .
      @Luigi . Dictionary is well thought but I found myself lost sometimes with Dict family in the past , and i could not deduct every problem from the documentation or the forum to be honest . Ive been training in another thread the namesapces and nesting that required wider experience than just passing Dict to another (there was some unexpected situations due to names and instances). From SDK ive gained better understanding , but it wasnt an easy task to get over it and dig into every possible strength by the C beginner . Now we experienced that Array of Dicts is not possible in Javascript or it require some other habits to achieve such task that i cant realize at this very moment . But your thread was a gem to me when i found it . thanks a lot .
    • Sep 28 2014 | 8:04 pm
      I want to point out to the JSOBJ to DICT function implemented in tcpigola-dict.maxpat + tcpigola-dict.js I am far from saying it's perfect but works great for vector data... it will use ARRAYS for "SIMPLEARRAYS" where they work. For other types of arrays cointaining at least one object it will fallback to a dict.
      See it on www.leapformax.com
    • Sep 28 2014 | 11:24 pm
      Sorry (as the OP) for being silent on this for so long. I actually posted a reply to Luigi ages ago and for some reason it hasn't appeared.
      Anyway, I had already seen that post Luigi refers to, and there are no equivalent functions to the c example in the js api for placing a dict in an array. So there does appear to be a limitation, perhaps there does exist something but it is not documented. Anyway, I was able to format what I wanted as a json string and write it to file.
      here's the code:
      //will concatenate one or more dicts as an array of objects, 
      //keyed with the second argument of the addParent function 
      //outputs a stringified version of the resultant composite objects
      
      outlets = 1;
      
      var currentChildArray;	
      var	parentDictArray = new Array();
      var	currentParent = new Object();
      var currentChild = new Object();
      
      function addParent(parentDictName, keyName) {	
          var parentDict = new Dict(parentDictName);	
      	currentParent = dict_to_jsobj(parentDict); //currentParent is a js object
      	if (keyName!=null)							//if keyName not provided
      	{
      		currentChildArray = new Array();
       		currentParent[keyName] = currentChildArray;	//assign global temp array currentChildArray to "items" key     
      	}
      	parentDictArray.push(currentParent);		//then add the parent to an array of parents
      }
      
      function addChild(childDictName) {	
          var childDict = new Dict(childDictName);	
        	currentChild = dict_to_jsobj(childDict); //convert to json object		
          currentChildArray.push(currentChild);	//add to array
      }
      
      function clear(){		//purge the array of sub-dicts
      	while (parentDictArray.length) { parentDictArray.pop(); }
      }
      
      function bang()
      {
      	output=JSON.stringify(parentDictArray); //OR add these args:, null, "\t"
      	outlet (0,  output);
      }
      
      function write(p){
      	var jase = JSON.stringify(parentDictArray,null,'\t');
      	var path = p;
      	var fout = new File(path,"write","TEXT");
      	if (fout.isopen) {
      		fout.eof = 0;
      		fout.writeline(jase);
      		fout.close();
      		post("\nJSON Write",path);
      	} else {
      		post("\ncould not create json file: " + path);
      	}
      }
      
      // returns or includes null if there is a dict without containing data.
      function dict_to_jsobj(dict) {
      	if (dict == null) return null;
      	var o = new Object();
      	var keys = dict.getkeys();
      	if (keys == null || keys.length == 0) return null;
      
      	if (keys instanceof Array) {
      		for (var i = 0; i < keys.length; i++)
      		{
      			var value = dict.get(keys[i]);
      			
      			if (value && value instanceof Dict) {
      				value = dict_to_jsobj(value);
      			}
      			o[keys[i]] = value;
      		}		
      	} else {
      		var value = dict.get(keys);
      		
      		if (value && value instanceof Dict) {
      			value = dict_to_jsobj(value);
      		}
      		o[keys] = value;
      	}
      
      	return o;
      }
      
      
    • Sep 29 2014 | 12:28 am
      and here's an example patch (it's specific to a particular usage, but may be adapted to be of use to someone, some time): also btw it is conceivable that if you write the json file to disk and then read it in again with a dict object it will work, but haven't actually tried it.
      btw thanks for the code leapformax, I'll have a look!
    • May 11 2015 | 6:02 am
      But, I want to generate this _valid_ structure:
      {
      	key : [object, object, object….]
      }
      
      with each object being a dict like this
      {
      	key : val, key : val, key : val
      }
      
      Sorry to revive an old thread, but I recently figured out how to set arrays of dicts with regular Max messages (and without the need to use JavaScript or C).  Really nice work though, FP. Thanks for sharing what you've made.
      The tricky (and undocumented as far as I can see) bit to storing an array of dicts comes down to using the 'setparse' message once a key is holding an array.
      Example of Max messages:
      set key1 value // Create a key and assign a value.
      set key2 anotherValue // Create another key and assign a value.
      append key1 // Crucial. Turn key1's value into an array.
      setparse key1[0] nestedKey1: 24 nestedKey2: 36 // Set the first item in key1's array to be a dictionary.
      append key1 * // Extend the array with dummy content. Repeat steps 4 and 5 and update array index as required.
      
      Creates:
      {
      	"key1" : [ 		{
      			"nestedKey1" : 23,
      			"nestedKey2" : 45
      		}
      , 		{
      			"nestedKey1" : 52,
      			"nestedKey2" : 11
      		}
       ],
      	"key2" : "anotherValue"
      }
      
      At the time of this writing, it's still quite easy to bring Max (7.0.3) down with poorly formed messages to the dict object, so either save often or use Max 7 and recover your work.