Category: Techniques and algorithms

  • Using enum values as strictly typed object keys


    In TypeScript, it’s often useful to define interfaces or complex (structured)
    types whose properties (or keys) may only be values of a previously defined
    enum type.

    Here’s a good example: an object declaring a set of buttons for a modal dialog.

    Instead of this…

    type DialogButtons = {
      yes?: boolean,
      no?: boolean,
      cancel?: boolean
    }
    
    interface IDialog {
      buttons: DialogButtons
    }
    
    const dialog: IDialog = {
      buttons: {
        yes: true,
        no: false
      }
    }
    
    console.log("yes" in dialog.buttons) // true
    console.log(dialog.buttons.yes) // true
    
    console.log("no" in dialog.buttons) // true
    console.log(dialog.buttons.no) // false
    
    console.log("cancel" in dialog.buttons) // false
    console.log(dialog.buttons.cancel) // undefined

    This works, but once we try to use these values in other contexts, the approach becomes difficult to use. What if we want to pass which button was pressed to a callback function?

    It would be cumbersome. Therefore…

    …we can use enum values.

    enum DialogButton {
      YES = "yes",
      NO = "no",
      CANCEL = "cancel"
    }
    
    interface IDialog {
      buttons: { [B in DialogButton]?: boolean },
      callback: (button: DialogButton) => void
    }
    
    const dialog: IDialog = {
      buttons: {
        [DialogButton.YES]: true,
        [DialogButton.NO]: false
      },
      callback(button) {
        console.log(button)
      }
    }
    
    console.log(DialogButton.YES in dialog.buttons) // true
    console.log(DialogButton.NO in dialog.buttons) // true
    console.log(DialogButton.CANCEL in dialog.buttons) // false
    
    console.log(dialog.buttons[DialogButton.YES]) // true
    console.log(dialog.buttons[DialogButton.NO]) // false
    console.log(dialog.buttons[DialogButton.CANCEL]) // undefined
    
    dialog.callback(DialogButton.YES) // yes
    dialog.callback(DialogButton.NO) // no
    dialog.callback(DialogButton.CANCEL) // cancel

    This uses the a TypeScript feature called Mapped Types
    and allows us to play around with the actual button values with a lot more flexibility, for example we could
    define an array of buttons.

    function renderButtons(buttons: [DialogButton]): [HTMLElement] {
      // ...
    }

    Code gist: https://gist.github.com/vpalos/0aab903ef607d6da31229b957d91d888.

    Enjoy!

  • Universal getter for plain JS objects.

    Many times in JavaScript we have to reach deep into objects to access fields that could possibly not be there at all; for example let’s assume I have a JS object called options into which I’ve just read an entire JSON configuration file. Trying to access a field like options.application.cache.enable directly will potentially fail (e.g. when any of the intermediate levels is missing)…

    if (options.application.cache.enable) {
        // do stuff
    }
    

    …therefore this kind of work will usually involve some kind of testing for the existence of each level, so as to gracefully handle any misbehaving data. Also, many times I’ve seen the following approach used in these scenarios which is quite bad for anything more than a single depth level:

    if (options && options.application && ...) {
        // do stuff
    }
    

    So, just as a little helper, here is a small method that may prove to be quite handy when having to deal with data whose structure we’re not certain of (here’s a proper Gist entry).
    (more…)

  • Quickly filtering large lists of texts.

    Recently, I had to make a mock-up of a filtering function (something I need for a project I work on) so I can show-case it around. And since this mock-up seems to impress some people, I decided to share this function with everybody so others may enjoy the “loot” :).

    Briefing

    The function (i.e. filter()) basically receives a query, a large list of text items and (optionally) some options. It then proceeds to filter that list based on the query and returns an array with the index numbers of the matching items and (optionally) an array with the marked texts of the matching items (i.e. highlighted captures). The documentation comment should provide all the needed insight on this thing.

    Demo

    Here you can play with the mocked-up demo (works on a list of ~23000 items).

    Source

    Since I usually store these short snippets of code among my gists, you can find this function here. You should always check the gist for the most up-to-date code!

    /**
     * Demo: http://vpalos.com/sandbox/filter.js
     *
     * A generic search algorithm designed for filtering (very) large lists of strings; when an input string
     * contains all the parts (words or characters; whitespace is ignored) of the query, spread-out over the text
     * then the string is considered to be a match. It works with the way internet browsers (e.g. Firefox, Google
     * Chrome) filter address-bar suggestions on user input. It is also quite fast; on my i7 laptop, filtering
     *     1) a list of ~23000 items takes around 50ms (yes, milliseconds!);
     *     2) a list of ~1 million text items took under 1 second.
     * It works both in NodeJS as well as in browser environments (so far I only tested FF and GC).
     *
     * It has two functioning modes:
     * 1) word-mode: each (whitespace-separated) word in the input query must be found **whole** in the text:
     *     e.g. "foo bar" will match "123foo456bar789" but not "f oo ba r";
     * 2) charater-mode: the input query is matched per-character (whitespace is completely ignored):
     *     e.g. "foo bar" will match "f o o b a r" and even "-f.oo-ba.r-".
     *
     * Options (values below are the defaults):
     * {
     *     "case": false,               // true: case-sensitive
     *                                  // false: case-insensitive
     *
     *     "mark": true,                // true: returns item numbers + matches with highlighted captures
     *                                  // false: returns item numbers only
     *
     *     "prefix": "<strong>",        // highlight prefix string
     *     "suffix": "</strong>",       // highlight suffix string
     *
     *     "word": true,                // true: whole-word mode
     *                                  // false: character mode
     *
     *     "limit": 0                   // limit results to this amount
     *                                  // 0 means return the whole result (unlimited)
     * }
     *
     * @param {string} query The search query, consisting of space-separated chunks of characters.
     * @param {string|array} items A string or an array of strings to filter; if a string is given then it is
     *                             first split into an array of individual lines of text and then filtered (note
     *                             that).
     * @param {string} prefix String which will come before every highlighted substring (i.e. capture).
     * @param {string} suffix String which will come after every highlighted substring (i.e. capture).
     * @return {object} Returns the following object with matched items information:
     *                  {
     *                      "items": [...],     // array of matched item-numbers
     *                      "marks": [...]      // if mark == true, array of matches with highlighted captures
     *                  }
     */
    function filter(query, items, options) {
    
        // option producer
        function option(name, value) {
            options = options || {};
            return typeof(options[name]) !== 'undefined' ? options[name] : value;
        }
    
        // prepare options
        var o_case   = option("case",   false);
        var o_mark   = option("mark",   true);
        var o_prefix = option("prefix", "<strong>");
        var o_suffix = option("suffix", "</strong>");
        var o_word   = option("word",   true);
        var o_limit  = option("limit",  0);
    
        // prepare query
        query  = o_case ? query : query.toLowerCase();
        query  = query.replace(/\s+/g, o_word ? ' ' : '');
        query  = query.replace(/(^\s+|\s+$)/g, '');
        query  = query.split(o_word ? ' ' : '');
        var ql = query.length;
    
        // prepare items
        if (typeof(items) === "string") {
            items = items.split('\n');
        }
    
        // prepare results
        var matches = {
            items: [],
            marks: []
        };
    
        // search
        for (var ii = 0, il = items.length; ii < il; ii++) {
    
            // prepare text
            var text = o_case ? items[ii] : items[ii].toLowerCase();
            var mark = "";
    
            // traverse
            var ti = 0;
            var wi = 0;
            var wl = 0;
            for (var qi = 0; qi < ql; qi++) {
                wl = query[qi].length;
                wi = text.indexOf(query[qi], ti);
                if (wi === -1) {
                    break;
                }
                if (o_mark) {
                    if (wi > 0) {
                        mark += items[ii].slice(ti, wi);
                    }
                    mark += o_prefix + items[ii].slice(wi, wi + wl) + o_suffix;
                }
                ti = wi + wl;
            }
    
            // capture
            if (qi == ql) {
                if (o_mark) {
                    mark += items[ii].slice(ti);
                    matches.marks.push(mark);
                }
                if (matches.items.push(ii) === o_limit && o_limit) {
                    break;
                }
            }
        }
    
        // ready
        return matches;
    }
    

    [Update] Thanks to vladpalos for pointing-out some flaws in my initial code which, in the end, led me to simply using String.indexOf() for finding sub-matches.

    So…

    Let me know how this can be improved!

  • JS classes for the masses.

    Following on an older post of mine, I decided to write a new article about an(other) implementation of OOP Classes in JavaScript. This is something that brings a lot of value from very few lines of code and I want to share it with everyone and see how it can be improved.

    First let’s see the code (you should check here for the latest version) and then I’ll try and explain the relevant benefits/features but jump straight to the goodies if you like, knock yourself out! 🙂

    /**
     * Class.js: A class factory.
     */
    function Class(members) {
    
        // setup proxy
        var Proxy = function() {};
        Proxy.prototype = (members.base || Class).prototype;
    
        // setup constructor
        members.init = members.init || function() {
            if (Proxy.prototype.hasOwnProperty("init")) {
                Proxy.prototype.init.apply(this, arguments);
            }
        };
        var Shell = members.init;
    
        // setup inheritance
        Shell.prototype = new Proxy();
        Shell.prototype.base = Proxy.prototype;
    
        // setup identity
        Shell.prototype.constructor = Shell;
    
        // setup augmentation
        Shell.grow = function(items) {
            for (var item in items) {
                if (!Shell.prototype.hasOwnProperty(item)) {
                    Shell.prototype[item] = items[item];
                }
            }
            return Shell;
        };
    
        // attach members and return the new class
        return Shell.grow(members);
    }
    

    Observations

    Each Class points to (via .prototype) a corresponding Proxy object that holds the class’s member fields. In turn, this Proxy points to (via .prototype) the Proxy of the inherited class (i.e. the super-Class) and so on. So, each Class points to its own Proxy which points to the ancestor Proxy and so on, until the top level ancestor is reached. (more…)

  • A simple(r) approach for class-based OOP in JavaScript.

    Like others before me, I found JavaScript’s prototype-based OOP support – while very powerful – quite cumbersome in some situations where I needed a certain structure or hierarchy of objects, situations in which one quickly comes to the conclusion that the best answer would be the concept of a Class.

    However, while I am well aware that the web is filled with various implementations of classical (i.e. class-based) object orientation in JavaScript, I can not help the feeling that they are over-engineered and over-complicated without need, quite often attempting to provide more functionality than is actually necessary. In short, I don’t feel they’re “classical” at all.

    This is why I started working on my own implementation of classical OOP  in JavaScript, which is shown below. Currently this is only a proposal, although I already started using it in one of my projects. Anyway, let’s look at it first then I’ll try and explain how it works.

    The Code

    /** Class factory. */
    function Class(members) {
    
        // proxy constructor
        var Proxy = function() {
            for (var item in members) {
                this[item] = members[item];
            }
        };
    
        // proxy inheritance
        Proxy.prototype = (members.base || Class).prototype;
    
        // class constructor
        var Build = members.init || function() {};
    
        // class inheritance
        Build.prototype = new Proxy();
        Build.prototype.base = Proxy.prototype;
        Build.prototype.constructor = Build;
    
        // ready
        return Build;
    }

    That is all, and this little function can have a whole world of nifty consequences like deep inheritance or access to the super-class via this.base (and others). But here is what happens: the basic principle at work here is the Proxy object, which essentially, contains all the members of our new class (the ones that are passed to the Class factory function as the members parameter). (more…)