{"id":1563,"date":"2012-03-09T20:27:30","date_gmt":"2012-03-10T02:27:30","guid":{"rendered":"http:\/\/www.wiredprairie.us\/blog\/?p=1563"},"modified":"2012-03-09T20:27:30","modified_gmt":"2012-03-10T02:27:30","slug":"knockout-js-asdictionary","status":"publish","type":"post","link":"https:\/\/www.wiredprairie.us\/blog\/index.php\/archives\/1563","title":{"rendered":"Knockout.JS: AsDictionary"},"content":{"rendered":"

I frequently find that I have an array of objects in JavaScript that I want to display in a particular order and also have the ability to quickly locate an object by an ID or a key (and not use the indexOf function). As my recent project is using Knockout.JS<\/a>, I decided to throw together a function that makes having keyed lookups based on an array simple to maintain.<\/p>\n

Here\u2019s an example ViewModel definition:<\/p>\n

var <\/span>ViewModel = function <\/span>() {\n    this<\/span>.uniqueNumber = ko.observable();\n    this<\/span>.list = ko.observableArray([]);      \n    this<\/span>.list_by_keys = this<\/span>.list.asDictionary('id'<\/span>);\n};<\/pre>\n

The definition includes an array, a unique number (more on that a bit later), the list (to which the code will bind the UI to), and finally the keyed list. <\/p>\n

After a few attempts at a good name, I settled for something that I hated the least. \"Smile\" In any case, usage is simple. <\/p>\n

After creating the observable array:<\/p>\n

this<\/span>.list = ko.observableArray([]); <\/pre>\n

The code creates a second field which will contain all of the objects in the original array, but in a quickly accessible index (thanks to the nature of JavaScript objects). <\/p>\n

this<\/span>.list_by_keys = this<\/span>.list.asDictionary('id'<\/span>);<\/pre>\n

In the preceding line, the asDictionary <\/strong>function (which I\u2019ve added to the observableArray definition as you\u2019ll see below) is used and passed the string \u2018id\u2019. The \u2018id\u2019 is the name of the property of the JavaScript object that is later added to the list that will contain the key (the primary key, although it\u2019s not checked for duplicates). <\/p>\n

As you\u2019ll note below, an instance of the ViewModel is created and bound to the UI.<\/p>\n

var <\/span>vm = new <\/span>ViewModel();\nko.applyBindings(vm);\n\n$("#btnAdd"<\/span>).on("click"<\/span>, function <\/span>() {\n    var <\/span>id = vm.uniqueNumber.inc()();\n    vm.list.push({ id: id, 
                   time: new <\/span>Date().toLocaleTimeString() });\n});<\/pre>\n
ko.observableArray.fn.asDictionary = function <\/span>(keyName) {\n    return <\/span>ko.computed(function <\/span>() {\n        var <\/span>list = this<\/span>() || [];    \/\/ the internal array\n        <\/span>var <\/span>keys = {};              \/\/ a place for key\/value\n        <\/span>ko.utils.arrayForEach(list, function <\/span>(v) {\n            if <\/span>(keyName) {          \/\/ if there is a key\n                <\/span>keys[v[keyName]] = v;    \/\/ use it\n            <\/span>} else <\/span>{\n                keys[v] = v;\n            }\n        });\n        return <\/span>keys;\n    }, this<\/span>);\n};<\/pre>\n

The function loops through each of the elements of the array and stores each object by the key (if provided, otherwise by the value).  Unfortunately, because there are many ways to adjust an array in JavaScript, this isn\u2019t as efficient as I\u2019d like. Every time something is added to the array, the entire \u201cdictionary\u201d is recreated. While this isn\u2019t terrible in reasonable cases, it\u2019s still a bit annoying. You could add a bit of code to disable the rebuilding conditionally though if performance is going to be a big concern. <\/p>\n

I also was experimenting with a unique number generator. It\u2019s really quite dumb, but I \u2018m posting in nonetheless.<\/p>\n

ko.observable.fn.inc = function <\/span>(incExtra) {\n    incExtra = incExtra || 1;            \n    var <\/span>current = this<\/span>() || 0;\n    current += incExtra;            \n    this<\/span>(current);\n    return this<\/span>;\n};\n\nko.observable.fn.dec = function <\/span>(decExtra) {\n    return <\/span>ko.observable.fn.inc(decExtra || -1);\n};<\/pre>\n

To use it and retrieve the value, call it like this:<\/p>\n

var <\/span>id = vm.uniqueNumber.inc()();<\/pre>\n

\"image\"The odd syntax calls the inc (increment) function which returns the original object (in support of chaining). Then, to get the value, it calls the properties\u2019 getter function (the second set of parentheses).  (As I said, it was just messing around).<\/p>\n

The HTML for the data binding looked like this:<\/p>\n

<div class="log" <\/span>data-bind="foreach: list" <\/span>>\n    <div class="item"<\/span>>\n        <span data-bind="text: id" <\/span>class="id"<\/span>><\/span>\n        <span data-bind="text: time"<\/span>><\/span>\n    <\/div>\n<\/div><\/pre>\n","protected":false},"excerpt":{"rendered":"

I frequently find that I have an array of objects in JavaScript that I want to display in a particular order and also have the ability to quickly locate an object by an ID or a key (and not use the indexOf function). As my recent project is using Knockout.JS, I decided to throw together […]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spay_email":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true},"categories":[4],"tags":[75,38,74],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/pd5QIe-pd","jetpack_likes_enabled":true,"jetpack-related-posts":[{"id":1565,"url":"https:\/\/www.wiredprairie.us\/blog\/index.php\/archives\/1565","url_meta":{"origin":1563,"position":0},"title":"Knockout.JS: Dictionary\/Index and ObservableArray, Part 2","date":"March 10, 2012","format":false,"excerpt":"Ryan suggested an alternative in a comment (and corresponding jsFiddle) to the technique that I\u2019d used in my previous Knockout.JS post. Following his suggestion, I made a few tweaks to my original function (and renamed it yet again): ko.observableArray.fn.withIndex = function (keyName) { var index = ko.computed(function () { var\u2026","rel":"","context":"In "Coding"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1820,"url":"https:\/\/www.wiredprairie.us\/blog\/index.php\/archives\/1820","url_meta":{"origin":1563,"position":1},"title":"Knockout binding for JavaScript route fixup","date":"January 25, 2013","format":false,"excerpt":"Part one. After the first round, I felt compelled to KnockOut the code a bit more. I\u2019d mentioned I wasn\u2019t pleased with the code exactly. It needed some refactoring. So, I\u2019ve created a new Knockout binding handler. This binding handler replaces\u00a0 named parameters with a model\u2019s properties in a path.\u2026","rel":"","context":"In "Coding"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1204,"url":"https:\/\/www.wiredprairie.us\/blog\/index.php\/archives\/1204","url_meta":{"origin":1563,"position":2},"title":"Return of syntax highlighting and code completion for KnockoutJS in VS2010 (when using Razor)","date":"March 31, 2011","format":false,"excerpt":"OK, admittedly, this is a workaround for an issue where the syntax of jQuery Templates (used by KnockoutJS) doesn\u2019t lend itself to the most pleasant editing experience in Visual Studio, but eh. This was inspired after talking with Ryan a bit and seeing a recent post on his new web\u2026","rel":"","context":"In "Coding"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1836,"url":"https:\/\/www.wiredprairie.us\/blog\/index.php\/archives\/1836","url_meta":{"origin":1563,"position":3},"title":"Smarter queuing of files using jQuery deferred and when","date":"February 13, 2013","format":false,"excerpt":"I had a somewhat simple need \u2026a single web page style application using Backbone.JS had multiple template files it needed to download, if and only if the files hadn\u2019t already been downloaded before. While I suppose I could have relied on browser caching, I wanted to manage the requests in\u2026","rel":"","context":"In "Coding"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1880,"url":"https:\/\/www.wiredprairie.us\/blog\/index.php\/archives\/1880","url_meta":{"origin":1563,"position":4},"title":"Using Windows CSCRIPT to compile a Handlebar.js template","date":"March 28, 2013","format":false,"excerpt":"I was looking for an alternative to using Node.JS for a JavaScript build process today on a Windows machine. I wanted something that relied as much on natively installed elements of a modern Windows PC as possible so that the build process would be portable. So, I broke out my\u2026","rel":"","context":"In "Coding"","img":{"alt_text":"image","src":"https:\/\/i0.wp.com\/www.wiredprairie.us\/blog\/wp-content\/uploads\/2013\/03\/image_thumb9.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":1800,"url":"https:\/\/www.wiredprairie.us\/blog\/index.php\/archives\/1800","url_meta":{"origin":1563,"position":5},"title":"Named routing with Knockout, ASP.NET MVC, and AttributeRouting, from JavaScript","date":"January 24, 2013","format":false,"excerpt":"It's hard to describe exactly what I've built here, but I'm just throwing these pieces out on the Internet in case someone: a) finds them useful, or b) has a better solution. Goals: Named routes, with a wee bit of a Rails feel A Knockout friendly syntax for binding to\u2026","rel":"","context":"In "Coding"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.wiredprairie.us\/blog\/index.php\/wpjson\/wp\/v2\/posts\/1563"}],"collection":[{"href":"https:\/\/www.wiredprairie.us\/blog\/index.php\/wpjson\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.wiredprairie.us\/blog\/index.php\/wpjson\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.wiredprairie.us\/blog\/index.php\/wpjson\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.wiredprairie.us\/blog\/index.php\/wpjson\/wp\/v2\/comments?post=1563"}],"version-history":[{"count":1,"href":"https:\/\/www.wiredprairie.us\/blog\/index.php\/wpjson\/wp\/v2\/posts\/1563\/revisions"}],"predecessor-version":[{"id":1564,"href":"https:\/\/www.wiredprairie.us\/blog\/index.php\/wpjson\/wp\/v2\/posts\/1563\/revisions\/1564"}],"wp:attachment":[{"href":"https:\/\/www.wiredprairie.us\/blog\/index.php\/wpjson\/wp\/v2\/media?parent=1563"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wiredprairie.us\/blog\/index.php\/wpjson\/wp\/v2\/categories?post=1563"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wiredprairie.us\/blog\/index.php\/wpjson\/wp\/v2\/tags?post=1563"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}

With a click of a button (using jQuery syntax), a new sample object containing an \u2018id\u2019 and \u2018time\u2019 property is added to the master list. When the new object is added, the asDictionary code is executed. Why? Because of the use of the computed<\/a> function as shown below. Knockout.JS has computed observables which automatically track dependencies and execute any time that the source property changes. In this case, it\u2019s tracking the \u201cthis\u201d object, which just happens to be the observableArray (list). <\/p>\n