locked
how can i limit items count in groups? (ContosoCookbook js) RRS feed

  • Question

  • (function () {
        "use strict";
        WinJS.xhr({ url: "/data/Recipes.txt" }).then(function (xhr) {
            var items = JSON.parse(xhr.responseText);
            // Add the items to the WinJS.Binding.List
            items.forEach(function (item) {
                list.push(item);
            });
        });
        var list = new WinJS.Binding.List();
        var groupCount = {};
        var maxItems = 2;
        var filteredList = list.createFiltered(function _filterItems(item) {
            groupCount[item.title] = groupCount[item.title] ? groupCount[item.title] + 1 : 1;
            return groupCount[item.title] <= maxItems;
        });
        var groupedItems = list.createGrouped(
            function groupKeySelector(item) { return item.group.key; },
            function groupDataSelector(item) { return item.group; }
        );
        WinJS.Namespace.define("Data", {
            items: groupedItems,
            groups: groupedItems.groups,
            getItemReference: getItemReference,
            getItemsFromGroup: getItemsFromGroup,
            resolveGroupReference: resolveGroupReference,
            resolveItemReference: resolveItemReference
        });
        // Get a reference for an item, using the group key and item title as a
        // unique reference to the item that can be easily serialized.
        function getItemReference(item) {
            return [item.group.key, item.title];
        }
        // This function returns a WinJS.Binding.List containing only the items
        // that belong to the provided group.
        function getItemsFromGroup(group) {
            return list.createFiltered(function (item) { return item.group.key === group.key; });
        }
        // Get the unique group corresponding to the provided group key.
        function resolveGroupReference(key) {
            for (var i = 0; i < groupedItems.groups.length; i++) {
                if (groupedItems.groups.getAt(i).key === key) {
                    return groupedItems.groups.getAt(i);
                }
            }
        }
        // Get a unique item from the provided string array, which should contain a
        // group key and an item title.
        function resolveItemReference(reference) {
            for (var i = 0; i < groupedItems.length; i++) {
                var item = groupedItems.getAt(i);
                if (item.group.key === reference[0] && item.title === reference[1]) {
                    return item;
                }
            }
        }
        // Returns an array of sample data that can be added to the application's
        // data list. 
    })();

    Hi,

     i wanted to limit item's count of each group to '2'. So i inserted the source like this

        var list = new WinJS.Binding.List();
        var groupCount = {};
        var maxItems = 2;
        var filteredList = list.createFiltered(function _filterItems(item) {
            groupCount[item.title] = groupCount[item.title] ? groupCount[item.title] + 1 : 1;
            return groupCount[item.title] <= maxItems;
        });

    but this is not working. Why? What more do I need?

    Please tell me about it...

    thank you for reading.

    As always, thank you.

    Saturday, January 26, 2013 1:39 PM

Answers

  • I figured that the same Item can be processed two times by the createFiltered function. Not sure why...

    For example, let's say we ask for 4 items. For the group 1, you'll see Item 1, Item 2, Item 1, Item 3, ... So you have reached the limit, but you only have 3 items, all the others will return false and won't be displayed.

    Well, I made another solution to make sure it doesn't count the same item twice. It's a bit more complex. It could probably be optimized but I'm not a Javascript ninja ^^

    So I used a Group 'object' to help me in that task. It keeps track of the items that are already counted.

    Now you just have to changed the maxCount value to change how many items you wan to see.

        var maxCount = 4;
        var groups = [];
        var Group = function Group(maxCount) {
            var self = this;
            self.limit = maxCount;
            self.items = [];
            self.Contains = function (item) {
                for (var i in self.items) {
                    if (self.items[i].title == item.title)
                        return true;
                }
                return false;
            };
            self.Add = function (item) {
                if (self.items.length < self.limit) {
                    self.items.push(item);
                    return true;
                }
                return false;
            }
        }
    
        var list = new WinJS.Binding.List();
        var groupedItems = list.createFiltered(function (item) {
            if (!groups[item.group.key]) {
                var group = new Group(maxCount);
                group.Add(item);
                groups[item.group.key] = group;
            } else {
                if (!groups[item.group.key].Contains(item)) {
                    return groups[item.group.key].Add(item);
                }
            }
            return true;
        }).createGrouped(
            function groupKeySelector(item) { return item.group.key; },
            function groupDataSelector(item) { return item.group; }
        );

    Notice also the Contains function in the Group class:

      self.Contains = function (item) {
                for (var i in self.items) {
                    if (self.items[i].title == item.title)
                        return true;
                }
                return false;
            };

    It compares items based on their title, because that's what they use in the Grid App template. So in this case, we suppose that Title is always unique. Otherwise we could have an issue and we could have more items than requested! So, of course if you have an ID property in your own Item class definition, it would be better to use it instead of the title.

    Best regards, 

    Renaud



    http://www.renauddumont.be

    • Marked as answer by 박대웅 Sunday, January 27, 2013 6:08 PM
    Sunday, January 27, 2013 12:06 AM
  • Hi 박대웅

    I tried the following code with the Grid App template in Visual Studio 2012 and it works fine.

    The aim of the code is to first filter the list. I count the number of items in each group. If there are 2 or more items, the item is omitted.

    The the list is grouped as it was before, based on the filtered list.

        var groupCount = [];
    
        var list = new WinJS.Binding.List();
        var groupedItems = list.createFiltered(function (item) {
            if (!groupCount[item.group.key]) {
                groupCount[item.group.key] = 1;
            } else {
                if (groupCount[item.group.key] >= 2) {
                    return false;
                }
                groupCount[item.group.key]++;
            }
            return true;
        }).createGrouped(
            function groupKeySelector(item) { return item.group.key; },
            function groupDataSelector(item) { return item.group; }
        );



    http://www.renauddumont.be


    • Edited by RenaudDumontMVP Saturday, January 26, 2013 8:37 PM
    • Marked as answer by 박대웅 Saturday, January 26, 2013 11:18 PM
    Saturday, January 26, 2013 8:36 PM

All replies

  • Hi 박대웅

    I tried the following code with the Grid App template in Visual Studio 2012 and it works fine.

    The aim of the code is to first filter the list. I count the number of items in each group. If there are 2 or more items, the item is omitted.

    The the list is grouped as it was before, based on the filtered list.

        var groupCount = [];
    
        var list = new WinJS.Binding.List();
        var groupedItems = list.createFiltered(function (item) {
            if (!groupCount[item.group.key]) {
                groupCount[item.group.key] = 1;
            } else {
                if (groupCount[item.group.key] >= 2) {
                    return false;
                }
                groupCount[item.group.key]++;
            }
            return true;
        }).createGrouped(
            function groupKeySelector(item) { return item.group.key; },
            function groupDataSelector(item) { return item.group; }
        );



    http://www.renauddumont.be


    • Edited by RenaudDumontMVP Saturday, January 26, 2013 8:37 PM
    • Marked as answer by 박대웅 Saturday, January 26, 2013 11:18 PM
    Saturday, January 26, 2013 8:36 PM
  • Thank you, Thank you so much. You save me!!!!!!!!!!!!!!!

    And can i ask one more question?

    if (groupCount[item.group.key] >= 2)

    this is your code..

    -   if (groupCount[item.group.key] >= 'this')   -

    If I insert number 2 to 'this', app's item count is 2. but number 3 is also 2.

     1-1, 2-2, 3-2, 4-3, 5-3, 6-4, 7-4, 8-5, 9-5, 10-6, 11-6....

    Do you know the reason of this match?

    (Really, thank you so much.)  

    Saturday, January 26, 2013 11:18 PM
  • I figured that the same Item can be processed two times by the createFiltered function. Not sure why...

    For example, let's say we ask for 4 items. For the group 1, you'll see Item 1, Item 2, Item 1, Item 3, ... So you have reached the limit, but you only have 3 items, all the others will return false and won't be displayed.

    Well, I made another solution to make sure it doesn't count the same item twice. It's a bit more complex. It could probably be optimized but I'm not a Javascript ninja ^^

    So I used a Group 'object' to help me in that task. It keeps track of the items that are already counted.

    Now you just have to changed the maxCount value to change how many items you wan to see.

        var maxCount = 4;
        var groups = [];
        var Group = function Group(maxCount) {
            var self = this;
            self.limit = maxCount;
            self.items = [];
            self.Contains = function (item) {
                for (var i in self.items) {
                    if (self.items[i].title == item.title)
                        return true;
                }
                return false;
            };
            self.Add = function (item) {
                if (self.items.length < self.limit) {
                    self.items.push(item);
                    return true;
                }
                return false;
            }
        }
    
        var list = new WinJS.Binding.List();
        var groupedItems = list.createFiltered(function (item) {
            if (!groups[item.group.key]) {
                var group = new Group(maxCount);
                group.Add(item);
                groups[item.group.key] = group;
            } else {
                if (!groups[item.group.key].Contains(item)) {
                    return groups[item.group.key].Add(item);
                }
            }
            return true;
        }).createGrouped(
            function groupKeySelector(item) { return item.group.key; },
            function groupDataSelector(item) { return item.group; }
        );

    Notice also the Contains function in the Group class:

      self.Contains = function (item) {
                for (var i in self.items) {
                    if (self.items[i].title == item.title)
                        return true;
                }
                return false;
            };

    It compares items based on their title, because that's what they use in the Grid App template. So in this case, we suppose that Title is always unique. Otherwise we could have an issue and we could have more items than requested! So, of course if you have an ID property in your own Item class definition, it would be better to use it instead of the title.

    Best regards, 

    Renaud



    http://www.renauddumont.be

    • Marked as answer by 박대웅 Sunday, January 27, 2013 6:08 PM
    Sunday, January 27, 2013 12:06 AM
  • This is working well. Sincerely, thank you so much. You are my hero.
    Sunday, January 27, 2013 6:08 PM
  • You're welcome ! I'm glad it helped :)

    http://www.renauddumont.be

    Sunday, January 27, 2013 7:37 PM