This might seem like an obvious thing to some but it struck me just how bloody easy it is to create multi-dimensional arrays in javascript when you’re using OData.

Pretty much you don’t need to do anything except make your OData call and it will take care of the rest.

As is always the way I was working on a project yesterday that inspired this blog post and solving a problem where my Ajax calls were getting a tad too chatty and slowing down the performance of my context menus.

Setting the scene

In my project I have a menu list of files and folders and each time you select one of the files the display is updated and the context menus re-configured depending on the state of the selected item.

This in turn was triggering a change on a dropdown menu that populated an unordered list of groups and categories.

Initially I was making the call to get a list of categories and groups. Each group itself has child records of type ImageOutput and each category has child records of type ImageOutput.

When a group was selected it would pop across to the server and get the list of ImageOutputs. This could happen many, many times but the Groups, Categories and ImageOutput lists will rarely, if ever change.


So caching this on the client side in a multidimensional array was an obvious choice.

OData to the rescue

So to solve this problem I could have manually created the array and made all the appropriate calls to the server to get each set of lists but that would involve multiple round trips to the server. Why not do it the easy way?

First off I have my helper function to make the Ajax calls:

function GetListData(url,async){ var list = ""; $.ajax({ type: "GET", url: url, async: async, contentType: "application/json; charset=utf-8", dataType: "json", success: function (msg) { list = msg.d; }, error: function (xhr) { ShowError(xhr); } }); return list; }

The above code just wraps the call to my OData urls and returns the results as json.

Armed with the wrapper function I just need make my calls to the OData urls that expose the data I am after. The first call to ImageOutputCategories just gets a simple list of categories.

The second call uses the $expand system query option which tells it to not only get the OutputGroups but also to return the associated ImageOutputs for each OutputGroup.

You can read more about the $expand system query option here.

if (categories null) { categories = GetListData("/DataServices/AMPLibrary.svc/ImageOutputCategories", false); } if (groups null) { groups = GetListData("/DataServices/AMPLibrary.svc/ImageOutputGroups?$expand=ImageOutputs", false); }

This second call will return me a multi-dimensional array that I can store on the client in memory and access whenever I please, no trip to the server required.

Some sample code below illustrates how the multi-dimensional array is used:

Populate the Group List:

$("#output-group").children().remove(); for (var i = 0; i <= groups.length - 1; i++) { var option = " + groups[i]["ImageOutputGroupName"] + ""; $("#output-group").append(option); if (groups[i]["Id"] $("#output-group").val()) { cropHeight = groups[i].CropHeight; cropWidth = groups[i].CropWidth; GetImageGroupOutputs(groups[i]["ImageOutputs"]); } }

Populate the OutputGroup list for the selected Group:

function GetImageGroupOutputs(imageOutputs){ $(".image-formats-row").children().remove(); var checkAll = '

'; $(".image-formats-row").append(checkAll); for (var i = 0; i <= categories.length - 1; i++) { if (imageOutputs.length>0) { var ul = "
    "for (var k = 0; k <= imageOutputs.length - 1; k++) { if (imageOutputs[k]["ImageOutputCategoryId"] categories[i]["Id"]) { var title = "" + categories[i]["CategoryName"] + ""; $(".image-formats-row").append(title); break; } } for (var k = 0; k <= imageOutputs.length - 1; k++) { if (imageOutputs[k]["ImageOutputCategoryId"] == categories[i]["Id"]) { var li = "
  • "; li += " + imageOutputs[k]["OutputName"] + "
  • "
    ; ul += li; } } ul+="
; $(".image-formats-row").append(ul); } } }


Thanks OData Team.