Alan Yong
Alan Yong

Reputation: 1033

custom orderby loop through nested array

my nested json is killing me, seem I have to write a custom filter to do orderBy

$scope.ranking = [

    {
        'uId': 2,
            'name': 'Jeremy',
            'tabs': [{
            'tabId': 1,
                'points': 100,
        }, {
            'tabId': 2,
                'points': 10
        }],

    },

    {
        'uId': 3,
            'name': 'Jordon',
            'tabs': [{
            'tabId': 1,
                'points': 180,
            },{
            'tabId': 2,
                'points': 5
        }],
    }]


}

what I want is sort users' ranking by points. It would be easy if the tabs contain single points, but multiple tabs indicate the types of game they played.

look at my fiddle then you'll know what I'm try to do, here http://jsfiddle.net/U4dd8/

I wrote my custom filter until here

app.filter("rankFilter", function(){
   return function (input, points) {
       var output = [];      

           for (var i in input) {

       for(var j in input[i].tabs){
        var points = input[i].tabs[j].points;

        output.push(points);

        }

       }   

    return output;
};

Upvotes: 0

Views: 198

Answers (2)

kubuntu
kubuntu

Reputation: 2535

First, since you already have currentTab in the view, you could select the users to display for each tab using ng-show instead of using extra filters. Then, use a custom filter (orderByPoints) to implement a custom sort that orders the users according to tab points.

Your view becomes

<div ng-controller="AppCtrl">
    <button ng-click="currentTab=1">shooting</button>
    <button ng-click="currentTab=2">dribble</button>
    <p>Leaderboard</p>
    <ul>
        <li ng-repeat="user in ranking | orderByPoints: currentTab"> 
            <span ng-repeat="tab in user.tabs">
                <span ng-show="tab.tabId == currentTab">
                    <a ng-click="showFriendsTasksEvent(user.uId)">
                        {{ user.name }} - {{ tab.points }}
                    </a>
                </span>
            </span>
        </li>
    </ul>
</div>

Custom filter

app.filter("orderByPoints", function () {
    var inputTab;
    function compare(a, b) {
        // Check for undefined since value may be missing for some users
        if (typeof a.tabs[inputTab] != 'undefined' && typeof b.tabs[inputTab] != 'undefined')
            return b.tabs[inputTab].points - a.tabs[inputTab].points;
        return 0;
    }
    // Filter function
    return function (input, currentTab) {
        inputTab = currentTab; // Get current tab
        return input.sort(compare); // Call custom sort
    }
});

Here is an updated Fiddle (I added an extra user and tab for demonstration).

Upvotes: 0

gkalpak
gkalpak

Reputation: 48211

If I understand correctly you want to display in each tab the users that have points for that category and sort them by points (for that specific category).

It's a very localized question (and rather not Angular-related), but here is a possible solution:

HTML:

<li ng-repeat="user in ranking | orderByPointsForTab:currentTab">
    <a ng-click="showFriendsTasksEvent(user.uId)">
        {{user.name}} {{user.tabs | pointsForTab:currentTab}}
    </a>
</li>

2 filters are used:

  1. pointsForTab: It takes a tabs array and the currentTab as arguments and returns the points associated with the current tab.
    This could as well be a function in the controller.

  2. orderByPointsForTab: It takes a users array and currentTab as arguments and returns an new array containing only the users that have points for the current tab and sorted in descending order of points (for the current tab).

In order to understand the code below, you should be familiar with the following methods of Array (standard JS - nothing Angular-specific):
- Array.prorotype.forEach
- Array.prorotype.some
- Array.prorotype.map
- Array.prorotype.sort

app.filter("orderByPointsForTab", function () {

    /* `users` and `currentTab` as input */
    return function (users, currentTab) {

        /* 1: Create an array of users that have points
         *    for current tab (in random order) */
        var unsortedUsersWithPoints = [];
        (users || []).forEach(function (user) {

            /* Iterate over the tabs of each user... */
            (user.tabs || []).some(function (tab) {

                /* ...until we find the current tab... */
                if (tab.tabId === currentTab) {

                    /* ...store the user and the points for current tab
                     * for later access (see below)... */
                    unsortedUsersWithPoints.push({
                        user: user,
                        points: tab.points
                    });

                    /* ...and break the loop for the current user */
                    return true;
                }
            });
        });

        /* 2: Sort the array of `{user, points}` objects 
         *    in descending order of points */
        var sortedUsersWithPoints = unsortedUsersWithPoints.sort(
            function (obj1, obj2) {
                return obj2.points - obj1.points;
            }
        );

        /* 3: Create a new array containing only `user` objects,
         *    but keeping the order in `sortedUsersWithPoints` (see above) */
        var output = sortedUsersWithPoints.map(function (obj) {
            return obj.user;
        });

        /* 4. Return the sorted `users` array */
        return output;
    };
});

The code described above can be found in this short demo.

Upvotes: 1

Related Questions