Wednesday, January 11, 2012

in JavaScript a hash is an object not an array and it doesn't carry a length property

I erroneously suggest here that hashes are arrays and that is NOT true. I learned that the hard way today. I was trying to sniff the contents of a dropdown list into a hash and then confirm that my code was working by trying to throw an alert onto the screen which would give the length of the "array" I was populating. I was expecting a result greater than zero, and when I got zero over and over again, no matter what I tried, I assumed that the rest of my code was messed up. However, the reality is that the alert below should return a zero and it is NOT because the hash is empty.

<html>

   <head>

      <script type="text/javascript" src="jquery-1.5.js">

      </script>

   </head>

   <body>

      Which Republican can still beat Romney?<br />

      <select id="dropdown">

         <option value="Egomaniac">Gingrich</option>

         <option value="Stoic">Huntsman</option>

         <option value="Ideologue">Paul</option>

         <option value="Redneck">Perry</option>

         <option value="Nazi">Santorum</option>

      </select>

      <script type="text/javascript">

         
var collection = new Array();

         $("#dropdown option").each(function () {

            collection[$(this).val()] = $(this).text();

         });

         alert(collection.length);


      </script>

   </body>

</html>

 
 

I got the idea for the very first line of my JavaScript, both above and below, from here, but I eventually realized that a hash is not really an array. Rather, you may sort of fudge a hash as an array and you may manually set the length parameter like so:

<html>

   <head>

      <script type="text/javascript" src="jquery-1.5.js">

      </script>

   </head>

   <body>

      Which Republican can still beat Romney?<br />

      <select id="dropdown">

         <option value="Egomaniac">Gingrich</option>

         <option value="Stoic">Huntsman</option>

         <option value="Ideologue">Paul</option>

         <option value="Redneck">Perry</option>

         <option value="Nazi">Santorum</option>

      </select>

      <script type="text/javascript">

         
var collection = new Array();

         collection.length = 0;

         $("#dropdown option").each(function () {

            collection[$(this).val()] = $(this).text();

            collection.length++;

         });

         alert(collection.length);


      </script>

   </body>

</html>

 
 

I hope you like how I am always correcting myself at my blog. Hey, I'm learning. Anyways, the author of this seems to be using array to be able to use length. I looked at other postings online and concluded that:

  1. a hash is an object and not an array in spite of how it may be manhandled
  2. there isn't an easy way to get a length of a hash
  3. if testing, fish out actual data like so:

    <html>

       <head>

          <script type="text/javascript" src="jquery-1.5.js">

          </script>

       </head>

       <body>

          Which Republican can still beat Romney?<br />

          <select id="dropdown">

             <option value="Egomaniac">Gingrich</option>

             <option value="Stoic">Huntsman</option>

             <option value="Ideologue">Paul</option>

             <option value="Redneck">Perry</option>

             <option value="Nazi">Santorum</option>

          </select>

          <script type="text/javascript">

             
    var collection = new Object();

             $("#dropdown option").each(function () {

                collection[$(this).val()] = $(this).text();

             });

             alert(collection['Ideologue']);


          </script>

       </body>

    </html>

a utility I wrote and its test

the utility:

using System;

using System.Collections.Generic;

using System.Linq;

using OurApp.Core.Entities;

namespace OurApp.Web.UI.Util

{

   public class ProgramPlanShuffleListFiltrationHelper

   {

      public string SerializedProgramPlansForPrograms { get; private set; }

      public string SerializedProgramPlansForSampleTypes { get; private set; }

      private Dictionary<Program, List<ProgramPlan>> _programPlansForPrograms;

      private Dictionary<SampleType, List<ProgramPlan>>

            _programPlansForSampleTypes;

   

      public ProgramPlanShuffleListFiltrationHelper(List<Program> programs,

            List<ProgramPlan> programPlans, List<SampleType> sampleTypes)

      {

         _programPlansForPrograms = programs.ToDictionary(program => program, program

               => new List<ProgramPlan>());

         _programPlansForSampleTypes = sampleTypes.ToDictionary(sampleType =>

               sampleType, sampleType => new List<ProgramPlan>());

         PopulateProgramPlansForPrograms(programPlans);

         PopulateProgramPlansForSampleTypes(programPlans, sampleTypes);

         MakeDataTransferObjectForPrograms(programs);

         MakeDataTransferObjectForSampleTypes(sampleTypes);

      }

   

      private void PopulateProgramPlansForPrograms(IEnumerable<ProgramPlan>

            programPlans)

      {

         foreach (ProgramPlan programPlan in programPlans)

         {

            List<ProgramPlan> dummyList =

                  _programPlansForPrograms[programPlan.Program];

            dummyList.Add(programPlan);

            _programPlansForPrograms[programPlan.Program] = dummyList;

         }

      }

   

      private void PopulateProgramPlansForSampleTypes(IEnumerable<ProgramPlan>

            programPlans, ICollection<SampleType> sampleTypes)

      {

         foreach (ProgramPlan programPlan in programPlans)

         {

            foreach (SampleType sampleType in programPlan.SampleTypes)

            {

               if (sampleTypes.Count > 0 && sampleTypes.Contains(sampleType))

               {

                  List<ProgramPlan> dummyList

                        = _programPlansForSampleTypes[sampleType];

                  dummyList.Add(programPlan);

                  _programPlansForSampleTypes[sampleType] = dummyList;

               }

            }

         }

      }

   

      private void MakeDataTransferObjectForPrograms(IEnumerable<Program> programs)

      {

         Dictionary<Guid, Dictionary<Guid, string>> outerDictionary = new Dictionary<Guid,

               Dictionary<Guid, string>>();

         foreach (Program program in programs)

         {

            Dictionary<Guid, string> innerDictionary = new Dictionary<Guid, string>();

            foreach (ProgramPlan programPlan in _programPlansForPrograms[program])

            {

               string name = program.Name + "." +

                     programPlan.NameStaticAttribute.

                     GetSelectedValueWithDefault(programPlan.Id).Value;

               innerDictionary.Add(programPlan.Id, name);

            }

            outerDictionary.Add(program.Id, innerDictionary);

         }

         SerializedProgramPlansForPrograms =

               Newtonsoft.Json.JsonConvert.SerializeObject(outerDictionary);

      }

   

      private void MakeDataTransferObjectForSampleTypes(IEnumerable<SampleType>

            sampleTypes)

      {

         Dictionary<Guid, List<Guid>> dictionary = new Dictionary<Guid, List<Guid>>();

         foreach (SampleType sampleType in

               sampleTypes)

         {

            List<Guid> list = new List<Guid>();

            foreach (ProgramPlan programPlan in

                  _programPlansForSampleTypes[sampleType])

            {

               list.Add(programPlan.Id);

            }

            dictionary.Add(sampleType.Id, list);

         }

         SerializedProgramPlansForSampleTypes =

               Newtonsoft.Json.JsonConvert.SerializeObject(dictionary);

      }

   }

}

 
 

the test:

using System.Collections.Generic;

using OurApp.Core.Entities;

using OurApp.Data.Tests.Support;

using OurApp.Web.UI.Util;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace OurApp.Data.Tests.Utils

{

   [TestClass]

   public class ProgramPlanShuffleListFiltrationHelperTest : DataIntegrationTestBase

   {

      [TestMethod]

      public void test_facets_of_ProgramPlanShuffleListFiltrationHelper()

      {

         
//arrange

         Category silicon = new Category { Name = "Silicon", IdPrefix = "S" };

         silicon.Save();

         SampleType cpu = new SampleType { Name = "CPU", IdPrefix = "CPU", Category

               = silicon };

         cpu.Save();

         SampleType gpu = new SampleType { Name = "GPU", IdPrefix = "GPU", Category

               = silicon };

         gpu.Save();

         SampleType chipset = new SampleType { Name = "Chipset", IdPrefix = "Chipset",

               Category = silicon };

         chipset.Save();

   

         ProgramPlan alpha = new ProgramPlan { CanOrder = true, SampleTypes = new

               List<SampleType> { cpu, gpu, chipset } };

         alpha.Save();

         alpha.NameStaticAttribute.CreateAndAddAvailableValue("Alpha")

               .SelectValueForEntity(alpha.Id);

         alpha.Save();

   

         ProgramPlan beta = new ProgramPlan { CanOrder = true, SampleTypes = new

               List<SampleType> { cpu } };

         beta.Save();

         beta.NameStaticAttribute.CreateAndAddAvailableValue("Beta")

               .SelectValueForEntity(beta.Id);

         beta.Save();

   

         ProgramPlan gamma = new ProgramPlan { CanOrder = true, SampleTypes = new

               List<SampleType> { cpu, gpu } };

         gamma.Save();

         gamma.NameStaticAttribute.CreateAndAddAvailableValue("Gamma")

               .SelectValueForEntity(gamma.Id);

         gamma.Save();

   

         ProgramPlan delta = new ProgramPlan { CanOrder = true, SampleTypes = new

               List<SampleType> { gpu } };

         delta.Save();

         delta.NameStaticAttribute.CreateAndAddAvailableValue("Delta")

               .SelectValueForEntity(delta.Id);

         delta.Save();

   

         ProgramPlan epsilon = new ProgramPlan { CanOrder = true, SampleTypes = new

               List<SampleType> { cpu, gpu } };

         epsilon.Save();

         epsilon.NameStaticAttribute.CreateAndAddAvailableValue("Epsilon")

               .SelectValueForEntity(epsilon.Id);

         epsilon.Save();

   

         ProgramPlan zeta = new ProgramPlan { CanOrder = true, SampleTypes = new

               List<SampleType> { cpu, gpu } };

         zeta.Save();

         zeta.NameStaticAttribute.CreateAndAddAvailableValue("Zeta")

               .SelectValueForEntity(zeta.Id);

         zeta.Save();

   

         ProgramPlan eta = new ProgramPlan { CanOrder = true, SampleTypes = new

               List<SampleType> { cpu, gpu, chipset } };

         eta.Save();

         eta.NameStaticAttribute.CreateAndAddAvailableValue("Eta")

               .SelectValueForEntity(eta.Id);

         eta.Save();

   

         Program moneyMaker = new Program { Name = "MoneyMaker", ProgramPlans =

               new List<ProgramPlan>() { alpha, beta, gamma, delta } };

         moneyMaker.Save();

         Program crowdPleaser = new Program { Name = "CrowdPleaser", ProgramPlans =

               new List<ProgramPlan>() { epsilon, zeta, eta } };

         crowdPleaser.Save();

   

         alpha.Program = moneyMaker;

         alpha.Save();

         beta.Program = moneyMaker;

         beta.Save();

         gamma.Program = moneyMaker;

         gamma.Save();

         delta.Program = moneyMaker;

         delta.Save();

         epsilon.Program = crowdPleaser;

         epsilon.Save();

         zeta.Program = crowdPleaser;

         zeta.Save();

         eta.Program = crowdPleaser;

         eta.Save();

   

         
//act

         ProgramPlanShuffleListFiltrationHelper programPlanShuffleListFiltrationHelper =

               new ProgramPlanShuffleListFiltrationHelper(new List<Program> { moneyMaker,

               crowdPleaser }, new List<ProgramPlan> { alpha, beta, gamma, delta, epsilon,

               zeta, eta }, new List<SampleType> { cpu, gpu, chipset });

   

         
//assert

         Assert.AreEqual(programPlanShuffleListFiltrationHelper.

               SerializedProgramPlansForPrograms, "{\"" + moneyMaker.Id + "\":{\"" +

               alpha.Id + "\":\"MoneyMaker.Alpha\",\"" + beta.Id + "\":\"MoneyMaker.Beta\",\""

               + gamma.Id + "\":\"MoneyMaker.Gamma\",\"" + delta.Id +

               "\":\"MoneyMaker.Delta\"},\"" + crowdPleaser.Id + "\":{\"" + epsilon.Id +

               "\":\"CrowdPleaser.Epsilon\",\"" + zeta.Id + "\":\"CrowdPleaser.Zeta\",\""

               + eta.Id + "\":\"CrowdPleaser.Eta\"}}");

         Assert.AreEqual(programPlanShuffleListFiltrationHelper.

               SerializedProgramPlansForSampleTypes, "{\"" + cpu.Id + "\":[\"" + alpha.Id +

               "\",\"" + beta.Id + "\",\"" + gamma.Id + "\",\"" + epsilon.Id + "\",\"" + zeta.Id +

               "\",\"" + eta.Id + "\"],\"" + gpu.Id + "\":[\"" + alpha.Id + "\",\"" + gamma.Id +

               "\",\"" + delta.Id + "\",\"" + epsilon.Id + "\",\"" + zeta.Id + "\",\"" + eta.Id + "\"],\""

               + chipset.Id + "\":[\"" + alpha.Id + "\",\"" + eta.Id + "\"]}");

      }

   }

}

embarassed by the ghost of JavaScript Past

I wrote some unforgivable JavaScript in an MVC3 view here. I've been asked to clean it up. My code for my first pass is below. I mostly got away from using arrays in favor of using hashes. In the one place where I do use an array, I use push to put things into the array instead of the terrible, ghetto approach I was undertaking in which I would:

  1. go through a loop adding commas to a string
  2. make an array by splitting the string, thus defining the size of the array
  3. go through back through the loop I had just been through in order to populate the slots in the array
Share photos on twitter with Twitpic
<body onload="boo()">

 
 

I did the steps above in the misguided assumption that an array in JavaScript was like an array in C# and I would hence have to concretely define the length of the array at its instantiation. This was not so. Arrays may grow over time in JavaScript. The code below functions a little differently than what the code here did. I'll explain later.

 
 

@using AMD.Avalanche.Web.UI.Util

@model ProgramPlanShuffleListFiltrationHelper

<script type="text/javascript">

   var dtoPrograms =

         jQuery.parseJSON('@Html.Raw(Model.SerializedProgramPlansForPrograms)');

   var dtoSampleTypes =

         jQuery.parseJSON('@Html.Raw(Model.SerializedProgramPlansForSampleTypes)');

   var endList = new Object();

   var oldList = new Object();

   

   $(function () {

      setupProgramPlansShuffleList();

      $('#SelectedSampleTypeId').keypress(function (e) {

         switch (e.keyCode) {

            case 38:

               adjustProgramPlansShuffleList();

               break;

            case 40:

               adjustProgramPlansShuffleList();

               break;

            case 13:

               adjustProgramPlansShuffleList();

               break;

         }

      }).focus(function () {

         adjustProgramPlansShuffleList();

      }).click(function () {

         adjustProgramPlansShuffleList();

      });

      $('#programsGroup-available').bind('onSelectedChanged', function () {

         adjustProgramPlansShuffleList();

      });

      $('#programsGroup-selection').bind('onSelectedChanged', function () {

         adjustProgramPlansShuffleList();

      });

      $('#programsGroup-to-selected').focus(function () {

         adjustProgramPlansShuffleList();

      }).click(function () {

         adjustProgramPlansShuffleList();

      });

      $('#programsGroup-all-to-selected').focus(function () {

         adjustProgramPlansShuffleList();

      }).click(function () {

         adjustProgramPlansShuffleList();

      });

      $('#programsGroup-all-to-available').focus(function () {

         adjustProgramPlansShuffleList();

      }).click(function () {

         adjustProgramPlansShuffleList();

      });

      $('#programsGroup-to-available').focus(function () {

         adjustProgramPlansShuffleList();

      }).click(function () {

         adjustProgramPlansShuffleList();

      });

   });

   

   function adjustProgramPlansShuffleList() {

      cleanUpNameBasedFiltration();

      prepareProgramPlanList(dtoPrograms, dtoSampleTypes);

      revampOldList();

      revampCurrentlyAvailableProgramPlans();

      revampOldList();

      revampCurrentlySelectedProgramPlans();

   }

   

   function setupProgramPlansShuffleList() {

      cleanUpNameBasedFiltration();

      prepareProgramPlanList(dtoPrograms, dtoSampleTypes);

      setupOldList();

      setupCurrentlySelectedProgramPlans();

      setupOldList();

      setupCurrentlyAvailableProgramPlans();

   }

   

   function cleanUpNameBasedFiltration() {

      $('#programsPlansGroup-available-clear').click();

      $('#programPlansGroup-selection-clear').click();

      $('#programPlansGroup-available-filter').val('');

      $('#programPlansGroup-selection-filter').val('');

   }

   

   function revampOldList() {

      oldList = new Object();

      $("#programPlansGroup-available option").each(function () {

         oldList[$(this).val()] = $(this).text();

      });

   }

   

   function setupOldList() {

      oldList = new Object();

      $("#programPlansGroup-selection option").each(function () {

         oldList[$(this).val()] = $(this).text();

      });

   }

   

   function revampCurrentlyAvailableProgramPlans() {

      var subList = new Object();

      $.each(endList, function (outerkey, outervalue) {

         $.each(oldList, function (innerkey, innervalue) {

            if (outerkey == innerkey) {

               subList[outerkey] = outervalue;

            }

         });

      });

      $("#programPlansGroup-available option").remove();

      $.each(subList, function (key, value) {

         $("#programPlansGroup-available").append($('<option>

               </option>').val(key).html(value));

      });

   }

   

   function revampCurrentlySelectedProgramPlans() {

      var subList = new Object();

      $.each(endList, function (outerkey, outervalue) {

         var noMatch = true;

         $.each(oldList, function (innerkey, innervalue) {

            if (outerkey == innerkey) {

               noMatch = false;

            }

         });

         if (noMatch) {

            subList[outerkey] = outervalue;

         }

      });

      $("#programPlansGroup-selection option").remove();

      $.each(subList, function (key, value) {

         $("#programPlansGroup-selection").append($('<option>

               </option>').val(key).html(value));

      });

   }

   

   function setupCurrentlyAvailableProgramPlans() {

      var subList = new Object();

      $.each(endList, function (outerkey, outervalue) {

         var noMatch = true;

         $.each(oldList, function (innerkey, innervalue) {

            if (outerkey == innerkey) {

               noMatch = false;

            }

         });

         if (noMatch) {

            subList[outerkey] = outervalue;

         }

      });

      $("#programPlansGroup-available option").remove();

      $.each(subList, function (key, value) {

         $("#programPlansGroup-available").append($('<option>

               </option>').val(key).html(value));

      });

   }

   

   function setupCurrentlySelectedProgramPlans() {

      var subList = new Object();

      $.each(endList, function (outerkey, outervalue) {

         $.each(oldList, function (innerkey, innervalue) {

            if (outerkey == innerkey) {

               subList[outerkey] = outervalue;

            }

         });

      });

      $("#programPlansGroup-selection option").remove();

      $.each(subList, function (key, value) {

         $("#programPlansGroup-selection").append($('<option>

               </option>').val(key).html(value));

      });

   }

   

   function prepareProgramPlanList(programsDto, sampleTypesDto) {

      var currentSampleType = $("#SelectedSampleTypeId option:selected").val();

      var currentPrograms = new Object();

      $("#programsGroup-selection option").each(function () {

         currentPrograms[$(this).val()] = $(this).text();

      });

      logic(programsDto, sampleTypesDto, currentSampleType, currentPrograms);

   }

   

   function logic(programsDto, sampleTypesDto, currentSampleType, currentPrograms) {

      endList = new Object();

      var sampleTypeMatchCollection = new Array();

      var matchMade = false;

      $.each(sampleTypesDto, function (key, value) {

         if (key == currentSampleType) {

            sampleTypeMatchCollection = value;

            matchMade = true;

         }

      });

      if (matchMade) {

         $.each(programsDto, function (outerkey, outervalue) {

            $.each(currentPrograms, function (midkey, midvalue) {

               if (outerkey == midkey) {

                  $.each(outervalue, function (innerkey,innervalue) {

                     var counter = 0;

                     $(sampleTypeMatchCollection).each(function () {

                        if (sampleTypeMatchCollection[counter] == innerkey) {

                           endList[innerkey] = innervalue;

                        }

                        counter++;

                     });

                  });

               }

            });

         });

      } else {

         endList["nomatch"] = "nomatch";

      }

   }

</script>

Closures in JavaScript

I'm on a walk over lunch. I was just talking with Rafael about Closures and coming to the conclusion that, as a concept, they are merely an assertion that variables instantiated in a function stay within that function and cannot be used beyond it unless the function returns one of the variables. If one function is nested inside of another then the outer function may not "see" variables instantiated in the inner function.

make a form that explicitly uses POST in Razor

@using (Html.BeginForm("Filter", "Order", FormMethod.Post, new { page = Model.ListView.Page, pageSize = Model.ListView.PageSize }))

Tuesday, January 10, 2012

Don't try to get away with polymorphism (overloading) for controller actions. It won't work.

Sigh.

take in JSON to craft a hash in jQuery

<html>

   <head>

      <script type="text/javascript" src="jquery-1.5.js">

      </script>

   </head>

   <body>

      <script type="text/javascript">   

         var json = jQuery.parseJSON('{"foo":"bar","baz":"qux"}');

         $.each(json, function(key, value) {

            alert(key);

            alert(value);

         });

      </script>

   </body>

</html>

 
 

The code above will create four alert boxes. They are:

  • foo
  • bar
  • baz
  • quz

 
 

Also, this line of code:

         var json = jQuery.parseJSON('{"foo":"bar","baz":"qux"}');

 
 

...may be replaced with something like this if one wants to bring in content from Razor:

         var json = jQuery.parseJSON('@Html.Raw(Model.Whatever)');

 
 

I've tried to break both versions of this line into two lines today. I have tried to put the JSON into a variable and then put the variable inside of the parseJSON call, but I have had no luck in getting it to work.