Javascript associative arrays



  • I'm having trouble in the design phase of a current project.  The requirement is that one drop-down box ("drop-3") should only have values that are valid for what's currently selected in the other two drop-downs ("drop-1") and ("drop-2").  The values in drop-1 and drop-2 are (almost) always going to be the same, but the valid values for drop-3 may vary pretty wildly from month to month.  

    AJAX and plain old Javascript/DOM manipulation spring to mind, but it requires enough code and is complex enough that someone else probably did it and put it into public domain.  By "complex enough" I mean that I don't know much Javascript and I could hard-code the whole thing, but I'd rather just use an API created by someone else.  

    Has anyone done anything like this before?  Should I just bite the bullet and do it all myself?  Is there an open-source (or otherwise free) API that will do this for me?

    Thanks in advance. 



  • I had something similar with a shipping interface where you could pick date of delivery, which affected the shipping methods available. Didn't find any APIs for it, but the Javascript came out at around 50 lines or so. A bit of validation, and some stuff to generate a new list of <options> based on the contents of an array, filtered by what was set in the date selector. 



  • @MarcB said:

    I had something similar with a shipping interface where you could pick date of delivery, which affected the shipping methods available. Didn't find any APIs for it, but the Javascript came out at around 50 lines or so. A bit of validation, and some stuff to generate a new list of <options> based on the contents of an array, filtered by what was set in the date selector. 

    Did you have javascript code generation on the server side?

    I'm giong to assume 50 lines is just the manipulation code and the code to populate the arrays is not included in that.  Is that correct?

    Thx by the way 



  • It was actually PHP spitting out some Javascript (mostly filling in array data), and a couple variables to make sure server-side data and client-side data were in-sync at the time of page-load. The rest of the 40-ish lines were mostly loops to iterate the array and build/swap the <options> arrays in and out of the appropriate places. 

    Now that I look at it again, a lot of the code was just "fluff". This particular project was for a very specific market test, and UPS was the only shipper. You could choose a delivery date, and depending on your location, that would determine if Saturday delivery was an option or not, so most of that code was to enable/disable Saturdays in the date picker calendar. 

    The critical loop was pretty simple. Iterate over a list of days (this was a time-limited test last December as well), and if the day in question was valid (ie: UPS could deliver on that day) then add the day to the dropdown list. It basically looked like:

    days = new Array(.....); // List of valid delivery dates, generated in PHP


    daysel = document.getElementById('yadayada'); // Dropdown to work on
    daysel.options.length = 0; // Reset options list to empty
    daysel.options[0] = new Option('', ''); // Blank option to start with

    for (d = 1; d < days.length; d++) {
        ... yada yada yada...
        daysel.options[daysel.length] = new Option('Dec ' + d + ', 2007 - ' + days[d], d); // only get here if 'd' is a valid day
    }

    It was kinda overkill, but the client was all "oooooh" and "ahhhh" about the dynamic highlighting of error conditions and how things magically changed depending on how you changed options.



  • @MarcB said:

    It was kinda overkill, but the client was all "oooooh" and "ahhhh" about the dynamic highlighting of error conditions and how things magically changed depending on how you changed options.

    I love magic. 



  • One method would be to write out all of drop-3's valid option sets in advance. Another would be to do some ajaxy goodness onchange of drop-1 or 2. That's more web 2.1, I suppose. The essential code would remain the same, of course.



  • Create an onchange event handler for the two select boxes.

    The onchange handler would do something like this:

    JS CODE:

    var http = null;

    function onSelectBoxChanged()
    {
        var ddl1 = document.getElementById('DropDownList1');
        var ddl2 = document.getElementById('DropDownList2');
        var ddl3 = document.getElementById('DropDownList3');
       
        // Anytime you are using the same XmlHttpRequest object
        // multiple times, you need to abort it, or you will have issues in IE7   
        if (http)
            http.abort();   
        else   
            http = getHTTPObject();
       
        // This is the callback function when the AJAX call returns, it will get called 4 times for each stage of the response.
        http.onreadystatechange = function ()
            {
                // Is the Response Ready?
                if (http.readyState == 4)
                {
                    // The response is back
                    // For simplicities sake, lets just assume the server sent
                    // back a comma delimited list of values.
                   
                    // Clear the ddl3 select box               
                    ddl3.options.length = 0;
                               
                    // Take the CSV response and stick it into an array
                    var resultArray = http.responseText.split(',');
                   
                    // Add the options to DDL3
                    for (var i = 0; i < resultArray.length; i++)
                    {
                        var theOption = document.createElement('OPTION');
                        theOption.value = resultArray[i];
                        theOption.text = resultArray[i];
                        ddl3.options.add(theOption);
                    }
                   
                }
            }
       
        // Lets pretend you use PHP on the server, we are going to send the
        // two selected values to some script which will decide what goes in ddl3
        // note the injection vulnerability

        var ajaxUrl = "ServerRequestHandler.php?ddl1=" + ddl1.value + "&ddl2=" + ddl2.value;
        http.open("GET", ajaxUrl, true);
        http.send(null);
       
    }

    // Cross Browser way to get the HTTP object
    function getHTTPObject()
        {
            if (window.XMLHttpRequest)
            {
                return new XMLHttpRequest();
            }
            else
            {
                if (window.ActiveXObject)
                {
                    return new ActiveXObject("Microsoft.XMLHTTP");
                }
            }
            return false;
        }

     

    Finally, your PHP script (or whatever) would simply echo its results to the output stream.



  •  I think using XHR would be serious overkill here, since you only need to fetch the values once when the page is loaded. My guess would be to use MarkB's solution, but with a key/value JSON expression instead of a numeric array. One way could be like this:

     var box3options = {
        "foo\nfoo": [ /* Array of options if box1 == foo and box2 == foo */ ],
        "foo\nbar": [ /* Array of options if box1 == foo and box2 == bar*/ ],
        "bar\nfoo": [ /* Array of options if box1 == bar and box2 == foo */ ],
        "bar\nbar": [ /* Array of options if box1 == bar and box2 == bar */ ]
        ...
    }

    Then you could populate box3 with something like

    var opt = box3options[box1.value + "\n" + box1.value];
    for (var i = 0; var i < opt.length; i++)
        //populate box3...

    The actual way you model the expression of course depends on how exactly the values in box3 depend on box1 and 2. The above list would grow quadratically if box1 and 2 both have many values. 

    You could either generate the expression inside the page itself, or, if you care about caching, generate it in its own file and link to it via a <script src="..."> tag.



  • It all depends on the number of values we are talking about.

     For a large list populated by say...a SQL Query, using XHR would be the best approach.

     If you have a largely static list of 10 or so elements, then I wouldn't bother.

     Remember, you can also do this:

     var myArray ={

    {Value: 'Foo', Text: 'Bar'},

    {Value: 'Foo2', Text: 'Bar2'},

    {Value: 'Foo3', Text: 'Bar3'}};

     

    And then access them like so:

     myArray[index].Value and myArray[index].Text

     



  • Arrays are instances of Objects, not the other way around. "myArray" is a distinctly false variable name.

    I loathe the term "associative array".



  • @dhromed said:

    Arrays are instances of Objects, not the other way around. "myArray" is a distinctly false variable name.

    I loathe the term "associative array".

     

    I loathe the term "object" because it doesn't mean anything. At least associative array has a generally accepted meaning. In JavaScript, everything (except primitive types) is an associative array, including functions and scopes.

    No two programmers will ever agree on what "object" means, but I dare say that it is typed, whereas JavaScript's associative arrays are fundamentally untyped. You can Add, Remove and Reassign (hence Retype) properties at will. The same associated property can be a string at one moment and a function the next. Other things commonly associated with objects such as inheritance and encapsulation simply do not exist in JavaScript (although you can fake it, using JS functions to emulate what in C++/Java/C# are classes).

    When dealing with JS, forget objects. It doesn't help that JS is littered with misplaced and misleading OO terminology, probably in an effort to be buzzword compatible (anno 1995).



  • @JvdL said:

    I loathe the term "object" because it doesn't mean anything.

    It does have a formal definition, although many people don't know it and it does mean very little.

    An object is an instance of a type (even if that type is trivial, like in an untyped language). Saying that something is an "object" is saying which order of things it belongs to: it is an object as opposed to being a type or a kind. And that is the only thing that it says.

    C++ example:

    foo<a>(b)

    The only thing we can say about a is that it is a type. The only thing we can say about b is that it is an object. (Note that all instances of primitive types like int are still considered to be objects)

    Except when working on compilers or language design or something similar, it is unlikely that this will be important.



  • Wow.  Thanks everyone for your responses. 

    After my last post in this thread, I decided to write it all myself. 

    I started with a removeAllOptions() function which accepted a SELECT object, then created an addOption which accepted a SELECT object and a string and added an option to that SELECT with value and text equal to the string.  The rest flowed from there and I showed the prototype to the user on Friday and he liked it.  I was surprised at how simple it all was for a JS novice like myself.

    haven't added AJAX yet, and I'm not sure it will be necessary.  I can put all the data into the page at load time and the user will just deal with the delay, but have instant drop-manipulation.  He only uses the app once every quarter or so anyways.


  • ♿ (Parody)

    @belgariontheking said:

    ...I decided to write it all myself.

     

    Excellent.  This has spawned so many WTFs for us in the past.  When will yours be ready? :P 



  • I'm not so elitist to say that my code is free from WTF.  I've written some pretty WTF stuff, but by and large my WTF stuff was for personal use, not comissioned (sp?) by anyone.  Mostly when I was learning a new language.

    I know you were jesting, but I outlined my design.  Are there any WTFs in that?  I can't post code because of NDA, sorry :)

    I think that rather than NIH, this is NBI (never been invented), a distinct subset of NIH.



  • @asuffield said:

    @JvdL said:

    I loathe the term "object" because it doesn't mean anything.

    It does have a formal definition, although many people don't know it and it does mean very little.

    An object is an instance of a type (even if that type is trivial, like in an untyped language). Saying that something is an "object" is saying which order of things it belongs to: it is an object as opposed to being a type or a kind. And that is the only thing that it says.

    So what do you say when it comes to systems where the type (i.e. class) is actually a constant/global that points to an object that describes the class?  In this case the type represents a concrete, live, instantiated object.

    JvdL was right in saying no two programmers will ever agree on the definition of "object."  :)



  • @JvdL said:

    When dealing with JS, forget objects. It doesn't help that JS is littered with misplaced and misleading OO terminology, probably in an effort to be buzzword compatible (anno 1995).

    Come on, don't "forget objects!"  JavaScript is a prototype-based language where objects use slots to store members (multiple entendre).  Use the language of the environment you're working in and things will go alot smoother.  When in Rome...

    You can thank the brilliant online web development community for trying to squeeze JS into terms of "objects" and "classes" in the Java sense.  The ECMA standard itself makes it quite clear, saying:

    ECMAScript does not contain proper classes such as those in C++, Smalltalk, or Java, but rather,
    supports constructors which create objects by executing code that allocates storage for the objects and
    initialises all or part of them by assigning initial values to their properties. All constructors are objects,
    but not all objects are constructors. Each constructor has a Prototype property that is used to implement
    prototype-based inheritance and shared properties.

     

    The "class" property of every object is simple equivalent to foo.prototype.toString() 



  • @djork said:

    So what do you say when it comes to systems where the type (i.e. class) is actually a constant/global that points to an object that describes the class?

     

    Don't confuse a type with an RTTI object that contains a duplicate of the type definition.



  • thread moved to "Coding Related Help & Questions" 



  • @ammoQ said:

    thread moved to "Coding Related Help & Questions" 

     

    Whew!  scared me there.  I went looking for my thread and couldn't find it!

    Should have originally posted it here in the first place though. 



  • @asuffield said:

    @djork said:

    So what do you say when it comes to systems where the type (i.e. class) is actually a constant/global that points to an object that describes the class?

    Don't confuse a type with an RTTI object that contains a duplicate of the type definition.

    Trust me, I'm not. I'm mainly talking about Ruby or Smalltalk here when I talk about classes that [i]are[/i] objects. Quite literally, the class of an object points to a real-live object which [i]is[/i] the type definition. In Smalltalk, you create a new class by sending a "subclass" message to the desired superclass, and the new class is returned.

    It's bizarre, I know... Check out Squeak, even if only to check out its object system. It's definitely worth it.



  • @djork said:

    Quite literally, the class of an object points to a real-live object which is the type definition

    The type definition is not the type. The source code is not the program. You're just muddling abstractions.

    None of the languages you mention have remotely unusual type systems. They're all roughly isomorphic to each other and to C++ and Java. You really don't see much variation in the type systems of programming languages - there's a few details here and there, like true polymorphism versus none at all, but nothing radical.



  • @asuffield said:

    @djork said:

    Quite literally, the class of an object points to a real-live object which is the type definition

    The type definition is not the type. The source code is not the program. You're just muddling abstractions.

    I see what you mean. Even when the [i]definition[/i] is the same object as what you get when you ask for the "type" of an object in Smalltalk, the "type" should be considered to be the [i]name[/i] of that definition instead.


Log in to reply