[JavaScript / TypeScript] BackboneのCollectionで複雑なソートをしたい

2014/05/8

こんにちは。きんくまです。

BackboneのCollectionで条件が2つ以上あるようなソートをしたいときのはなしです。

例)
従業員のプロパティが
・名前
・部署
・年齢

だったときに、Collectionのソート順を

1. 部署ごと
2. 同じ部署だったら年齢の高い順

にしようとしました。

とりあえずModelまで定義しちゃうとこんな感じになりました。
部署は名前じゃなくて、部署コードみたいな感じでenumで定義しときました。

module ex{
    export enum Division{
        Sales = 0
        ,ResearchAndDev
        ,Personnel
    }

    export class DivisionUtil{
        static names:string[] = [
            "営業部"
            ,"研究開発部"
            ,"人事部"
        ];

        static getNameByNumber(num:Division):string{
            return DivisionUtil.names[num];
        }
    }

    export class Employee extends Backbone.Model{
        name:string;
        age:number;
        division:Division;

        constructor(attrs?:any, options?:any){
            super(attrs, options);
        }

        initialize(){
            this.name = this.get('name');
            this.age = this.get('age');
            this.division = this.get('division');
        }

        toString(){
            return this.name + " (" + this.age + ") " + DivisionUtil.getNameByNumber(this.division);
        }
    }
}

それで、ここからが本題です。
公式のドキュメントによると、Collectionのsortをするときは、comparatorというプロパティが使われるみたいです。

>> comparator

デフォルトではcomparatorは入ってなくて、そのときaddするとソートはしない。
けどcomparatorを設定してあれば、addするときはそいつで自動ソートしてくれるみたい。
addするのに自動ソートなしでしたいってときは、{sort: false}をaddのオプションにいれればOK。

でcomparatorは以下の3種類からいづれかいれることができます。

1. 引数ひとつの関数
2. 引数ふたつの関数
3. 文字列(プロパティ名)

1と3はググるとサンプルでよく書いてあるけど、複雑なソートは2が良いと思います。
で、今回は条件が2つあるので、引数ふたつで書いてみました。
引数2つの関数はJavaScriptのネイティブのArrayのsortの引数と同じ形式で、数値の-1か0か1を返しておけば適当にやってくれます。
(私の場合、降べき昇べき順を頭で考えられないので、やってみて思い通りでなかったら、毎回1と-1のところを入れ替えて、しのいでますw)

module ex{
    export class EmployeesCollection extends Backbone.Collection{
        constructor(models?:any){
            super(models, {
                model:Employee
            });
        }

        initialize(){
            this.setComparator();
        }

        //ソート順を決める
        setComparator(){
            var emp1:Employee
                ,emp2:Employee;

            //年齢順のソート
            var sortByAge = (anEmp1:Employee, anEmp2:Employee)=>{
                if(anEmp1.age < anEmp2.age){
                    return 1;
                }else if(anEmp1.age > anEmp1.age){
                    return -1;
                }
                return 0;
            };

            //部署ごとのソート
            this.comparator = (m1:Backbone.Model, m2:Backbone.Model)=>{
                emp1 = <Employee>m1;
                emp2 = <Employee>m2;
                if(emp1.division < emp2.division){
                    return -1;
                }else if(emp1.division > emp2.division){
                    return 1;
                }
                //同じ部署だったら年齢順で
                return sortByAge(emp1, emp2);
            };
        }
    }
}

ためしてみます。

$(()=>{
    var employees = new ex.EmployeesCollection();

    employees.add([
        {name:"たなか", age:50, division:1}
        ,{name:"さとう", age:62, division:0}
        ,{name:"すずき", age:31, division:2}
        ,{name:"なかにし", age:23, division:1}
        ,{name:"ふくやま", age:36, division:1}
        ,{name:"ひらい", age:47, division:2}
        ,{name:"かねこ", age:33, division:0}
    ]);

    employees.models.forEach((elem)=>{
        console.log(elem.toString());
    });
});

出力結果はこうなりました。目的のソート順になったので良かったです。

さとう (62) 営業部
かねこ (33) 営業部
たなか (50) 研究開発部
ふくやま (36) 研究開発部
なかにし (23) 研究開発部
ひらい (47) 人事部
すずき (31) 人事部 
LINEで送る
Pocket

自作iPhoneアプリ 好評発売中!
フォルメモ - シンプルなフォルダつきメモ帳
ジッピー電卓 - 消費税や割引もサクサク計算!

ページトップへ戻る