Search code examples
knockout.jsknockout-2.0knockout-mvc

Knockout: Filtering arrays in array


I have the array of named items each of them contains other array of named items (some kind of tree). I need implementation of filtering by name in both arrays.

So I check name each item of the first array:

  • if the name is suitable I show entiry branch.
  • if the name isn't suitable I am checking sub array:
    • If a sub array doesn't contain suitable named items I don't show the branch.
    • If a sub array contains suitable named items I show branch with suitable items only.

I have implemented it in the next way:

ko.utils.stringStartsWith = function (string, startsWith) {
    if (startsWith.length > string.length)
        return false;

    return string.substring(0, startsWith.length) === startsWith;
};

$(function () {
    var vm = {
        search: ko.observable(''),
        items: ko.observableArray([])
    };

    //jsonData - my data
    $.each(jsonData, function (i, jItem) {
        var item = {
            name: jItem.Name,
            search: ko.observable(''),
            subItems: ko.observableArray([])
        };

        $.each(jItem.Items, function (j, jsubItem) {
            var subItem = {
                name: jsubItem.Name,
            };

            item.subItems.push(subItem);
        });

        item.filteredSubItems = ko.computed(function () {
                var self = this;

                return ko.utils.arrayFilter(this.subItems(), function (fsubItem) {
                    if (self.search().length == 0
                        || ko.utils.stringStartsWith(fsubItem.name.toLowerCase(), self.search().toLowerCase())) {
                        return true;
                    }

                    return false;
                });
            }, item);
        vm.items.push(item);
    });

    vm.filteredItems = ko.computed(function () {
        var self = this;

        return ko.utils.arrayFilter(this.items(), function (fitem) {
            if (self.search().length == 0
                || ko.utils.stringStartsWith(fitem.name.toLowerCase(), self.search().toLowerCase())) {
                fitem.search('');
                return true;
            }

            fitem.search(self.search());

            if (fitem.filteredSubItems().length != 0)
                return true;

            return false;
        });
    }, vm);

    ko.applyBindings(vm);
});

So it works well but calculating of filteredItems looks like ugly workaround for me. Moreover, I care about performance of my solution.

Does anybody know more glade solution for this?


Solution

  • I asked this question half of a year ago and I forgot to say that I found answer on this question. Nice and simple solution is Knockout Mapping.