第14話:配列のソートと無名関数

第2部 脱初心者プログラミング

目次

マンガ

マンガ

マンガ

マンガ

マンガ

マンガ

マンガ

マンガ

マンガ

マンガ台詞

【1Page】
アイコン麗:うーん 大変だわ
アイコン先生:どうしたんだ?
アイコン麗:生徒会で 各部活の予算を 決めているんですが 予算順に整理する のが大変で
アイコン遊:ふふふ 甘いわね麗!
    あんたはまだ プログラム脳に なりきれていないわ!
    こんな時こそ プログラムよ!
アイコン麗:むっ あなたは どうすれば 整理できるのか 分かるの?
アイコン遊:簡単よ
    先生にプログラムを 書かせればいいのよ!
アイコン先生:おいっ
【2Page】
アイコン先生:とはいえ プログラムで楽をしよう という考え方は正しい
    そういえば まだ『ソート』は 詳しく説明して いなかったな
アイコン遊:『ソート』って 何なの?
アイコン先生:配列を並び替える処理だ
    配列の『ソート』関数を使えば 値を辞書順に並べ替えてくれる
        var arr = ["dog", "cat", 900, 1000];    // 配列作成

        arr.sort(); // 辞書順にソート
        alert(arr); // 配列を表示

        arr.reverse();  // 向きを逆に
        alert(arr); // 配列を表示
    (辞書順)『1000,900,cat,dog』と表示
    (向きを逆にする)『dog,cat,900,1000』と表示
アイコン麗:書いていただいたのは ありがたいのですが
    これでは役に立ちません
【3Page】
アイコン先生:辞書順以外で 並べ替える場合は
    関数を引数にして 『ソート』する
    今回の問題を 解決する 実例を示そう
    詳しい解説は 次のページ から行うぞ
        // 配列
        var yosanArray = [
             {namae:"野球部", yosan:20000}
            ,{namae:"サッカー部", yosan:9000}
            ,{namae:"卓球部", yosan:12000}
            ,{namae:"写真部", yosan:15000}
        ];

        // ソート
        yosanArray.sort(
            function (a, b) {
                var aYosan = a["yosan"];
                var bYosan = b["yosan"];
                if (aYosan < bYosan) return -1;
                if (aYosan > bYosan) return 1;
                return 0;
            }
        );

        // 一覧表示
        for (var i = 0; i < yosanArray.length; i ++) {
            document.write(
                  i + " : "
                + yosanArray[i]["namae"]
                + ","
                + yosanArray[i]["yosan"] + "<br>"
            );
        }
    『0 : サッカー部,10000,
    1 : 卓球部,12000,
    2 : 写真部,15000,
    3 : 野球部,20000,』と表示
アイコン麗:なるほど これなら 部活名も分かり 予算順で便利ですね
【4Page】
アイコン先生:それではまず ソートの予備知識 を説明する
    『sort』メソッドの引数は 『function (a, b) {~}』 になっている
    このような 『名前のない関数』は 『無名関数』と呼ぶ
        配列.sort(function (a, b) {~});
        無名関数
アイコン先生:『sort』の引数の『無名関数』は 『a』『b』2つの引数を取る
    この引数を比較して 戻り値を戻せば 配列の順序が変わる
        戻り値 並べ替え
        0より小さい      小さいと見なして前に移動
        0               並べ替えなし
        0より大きい      大きいと見なして後ろに移動
アイコン先生:それでは実際の プログラムの 解説だ
    今回 ソート用のデータとして
    『yosanArray』という 配列を用意した
        var yosanArray = [  // 『yosanArray』配列
            // 配列の各要素はオブジェクトになっている
             {namae:"野球部", yosan:20000}     // 0番目の要素
            ,{namae:"サッカー部", yosan:9000}    // 1番目の要素
            ,{namae:"卓球部", yosan:12000}     // 2番目の要素
            ,{namae:"写真部", yosan:15000}     // 3番目の要素
        ];
【5Page】
アイコン先生:配列のソートを行うと
    『function (a, b) {~}』
    の引数『a』『b』に
    配列の要素であるオブジェクトが
    代入されて 関数が実行される
        {namae:"野球部", yosan:20000}
            {namae:"サッカー部", yosan:9000}
        yosanArray.sort(
            function (a, b) {
                引数『a』『b』の比較
アイコン先生:関数内の処理では
    引数『a』『b』を
    オブジェクトとして扱い
    値を取り出している
    あとは取り出した 2つの予算の値を 比較すればよい
        function (a, b) {
            var aYosan = a["yosan"];    // 値は『20000』
            var bYosan = b["yosan"];    // 値は『15000』
            if (aYosan < bYosan) return -1;  // 前に移動
            if (aYosan > bYosan) return 1;   // 後ろに移動
            return 0;   // 並べ替えなし
        }
アイコン先生:最後は出力部分だ
    オブジェクトの値を 『namae』『yosan』で 取り出している
        for (var i = 0; i < yosanArray.length; i ++) {
            document.write(
                  i + " : "
                + yosanArray[i]["namae"]
                + ","
                + yosanArray[i]["yosan"] + "<br>"
            );
        }
【6Page】
アイコン麗:比較を自由にできるなら いろいろと応用 できそうですね
先生&麗&アイコン守:(話している)
アイコン遊:うぐぐ 話についていけないわ 何かしゃべらないと
    先生!
    『sort』の『引数』は 『無名関数』ではなくて 名前のある関数でも いいのですか?
アイコン先生:遊くん!
アイコン遊:ひいっ ごめんなさい
    適当に言いました
【7Page】
アイコン先生:素晴らしい
    そうなんだ!
アイコン遊:へっ?
アイコン先生:ソート部分は こういう風に 書くこともできる
        yosanArray.sort(bukatuSort);

        function bukatuSort (a, b) {
            var aYosan = a["yosan"];
            var bYosan = b["yosan"];
            if (aYosan < bYosan) return -1;
            if (aYosan > bYosan) return 1;
            return 0;
        }
アイコン先生:こう書いてもいいぞ
        var bukatuSort = function (a, b) {
            var aYosan = a["yosan"];
            var bYosan = b["yosan"];
            if (aYosan < bYosan) return -1;
            if (aYosan > bYosan) return 1;
            return 0;
        };

        yosanArray.sort(bukatuSort);
【8Page】
アイコンアイコンアイコン3人:ぽかーん。
アイコン先生:はっ また 先走ってしまったか
遊&アイコン麗:ううう 私たち 先生に虐められて いるのかしら?
アイコン先生:あー すまん
    難しくならないように 気をつけるよ

説明

この章では、配列の『ソート』と『無名関数』について学びます。

配列を並び替えることを『ソート』と呼びます。配列は、その要素をを辞書順(辞書のような並び、1文字目から順に比較していく)に並べ替える『sort』メソッドを持っています。また、配列を逆順に並べ替える『reverse』メソッドも持っています。

『sort』メソッドは、引数を取り、特殊なルールで配列を並べ替えることもできます。『sort』メソッドは、引数として関数(『function』型オブジェクト)を取ります。そして、この関数内で、引数として与えられた2つの要素が『小さいか大きいか』を戻り値として戻すことで、並べ替えの順番を決めます。

『sort』メソッドの引数には、多くの場合『無名関数』と呼ばれる特殊な関数が使われます。『無名関数』は、『型、オブジェクト、クラス』の章で、軽く触れました。この『無名関数』は、名前を持たない関数です。

『無名関数』は多くの場合、『sort』メソッドなどの「関数を引数に取る関数」で、使い捨て用途で使われます。また『無名関数』は、変数に処理を格納する目的でも利用されます。

それでは以下、『ソート』と『無名関数』について解説していきます。

配列の『ソート』

配列オブジェクトには、いくつかのメソッドがあります。その中の1つである『sort』メソッドを使えば、配列を辞書順に並べ替えることができます。

var arr = ["dog", "cat", 900, 1000];  // 配列作成
arr.sort();  // 辞書順にソート
alert(arr);  // 配列を表示

【結果】

1000,900,cat,dog

また、『reverse』メソッドを使うことで、配列を逆の順番に並べ替えることができます。

arr.reverse();  // 向きを逆に
alert(arr);     // 配列を表示

【結果】

dog,cat,900,1000

それでは以下、ソートについて、さらに深く学んでいくことにしましょう。

引数を取る『ソート』

さて、この『sort』メソッドですが、引数として関数を取ることができます。

var yosan = [20000, 9000, 12000, 15000];    // 配列作成
yosan.sort(function (a, b) {/* 要素の比較 */});  // 関数を引数に設定

引数に指定した関数『function (a, b) {~}』は、『sort』メソッドが配列を並べ替える際に呼ばれます。

具体的にどのように呼ばれるのかを、手順として示します。

  1. 『sort』メソッドは、並べ替えのために比較したい2つの要素を選ぶ。
  2. 『sort』メソッドは、選んだ2つの要素を引数にして、関数を呼び出す。
    1. 呼ばれた関数は、第1引数に1つ目の要素を、第2引数に2つ目の要素を受け取って処理を開始する。
    2. 呼ばれた関数は、2つの要素を比較して、どちらが前か後かを、戻り値として戻す。
  3. 『sort』メソッドは、呼んだ関数の戻り値を元に、選んだ2つの要素を並べ替える。
  4. 『sort』メソッドは、次に比較したい要素があるか確認する。
    1. ある場合は最初に戻る。
    2. ない場合は処理を終了する。

引数として指定した関数は、呼び出される際に、2つの引数を取ります。この引き数は、『sort』メソッドが配列から選んだ2つの要素です。

上記のプログラムの例では、引数『a』『b』として、比較用の2つ要素を受け取ります。関数では、この引数『a』『b』を比較します。そして、『aはbの前か』『aはbの後ろか』を決めて、戻り値を戻します。この戻り値に従って、『sort』メソッドは、配列を並び替えていきます。

関数の戻り値として、『0より小さい値』を戻せば『a』を『b』より前に移動します。『0より大きい値』を戻した場合は、『a』を『b』の後ろに移動します。『0』を戻した場合は、並び順を変更しません。この対応表を、以下に示しておきます。

『ソート』は文字列やオブジェクトでも行えます。ここでは、数値を比較した場合について「値が数値の場合の意味」を参考に付けておきます。

戻り値 並べ替え 値が数値の場合の意味
0より小さい値 『a』を『b』より前に移動 『a』の方が小さいと判断
0 並べ替えなし 『a』と『b』は同じ
0より大きい値 『a』を『b』の後ろに移動 『a』の方が大きいと判断

戻り値として「0より小さいか大きいか」を戻せばよいので、数値の場合は単純に引き算を行なうことでソートできます。

var yosan = [20000, 9000, 12000, 15000];    // 配列作成
yosan.sort(function (a, b) {return a - b});
alert(yosan);

【結果】

9000,12000,15000,20000

『無名関数』

『sort』の引数として、『function (a, b) {~})』のような関数を指定しました。この関数は、『function』のあとに関数名がありません。このように、名前のついていない関数のことを『無名関数』と呼びます。

JavaScriptでは、この『無名関数』を多用します。この先、様々な関数の引数として、『無名関数』を利用することになります。そのためここで、『無名関数』の扱い方を覚えてください。

それでは以下、2つの利用シーンでの『無名関数』を解説します。

使い捨ての関数としての『無名関数』

『無名関数』のメリットは、使い捨ての関数を簡単に作れることです。この使い捨ての関数は、メソッドの引数として、よく利用されます。

以下、『無名関数』を利用したプログラムの例です。

// 『sort』の引数として、使い捨ての関数を作成して利用する
var no = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];   // 配列作成
no.sort(function (a, b) {
    // 偶数を前に、奇数を後ろにソートする
    if (a % 2 == 0 && b % 2 != 0) return -1;
    if (a % 2 != 0 && b % 2 == 0) return 1;
    return 0;
});
alert(no);  // 『2, 4, 6, 8, 10, 1, 3, 5, 7, 9』と表示

『function (a, b) {~}』という『無名関数』を、『sort』メソッドの引数としています。

変数に格納する『無名関数』

『無名関数』のもう1つの使い方は、変数に格納するというものです。たとえば、変数に格納しておけば、同じソート方法を、複数の場所で使うことができます。

以下、ソート用の関数を使い回す例です。

var fnc = function (a, b) {return a - b};

var yosan = [20000, 9000, 12000, 15000];
yosan.sort(fnc);
alert(yosan);    // 『9000,12000,15000,20000』と表示

var yosan2 = [80000, 700, 99000, 100000];
yosan2.sort(fnc);
alert(yosan2);   // 『700,80000,99000,100000』と表示

もう1つの例として、『型、オブジェクト、クラス』の章で出てきたプログラムを示します。

// 生徒クラスを作成
var Seito = function(namae, tensuu) {
    this.namae  = namae || "???";
    this.tensuu = tensuu || 0;
    this.show = function() {
        window.alert("名前 : " + this.namae + " / 点数 : " + this.tensuu);
    };
};

変数『this.show』に、『function() {~}』という『無名関数』を入れています。このように、一定の処理を変数として扱いたい時に『無名関数』は威力を発揮します。

また、『無名関数』は、クラスだけではなく、変数や関数を共通のオブジェクトの配下にまとめたい時にも利用されます。以下、その例です。

var myFunctions = new Object(); // オブジェクトを作成

myFunctions.alertTitle = function () {alert(document.title)};
        // 『alertTitle』というプロパティに、関数を代入

myFunctions.alertTitle();
        // 『alertTitle』メソッドを実行して、Webページのタイトルを表示する

変数『myFunctions.alertTitle』に、『無名関数』を代入しています。

参考

マンガ中のプログラムの解説

それでは以下、マンガ中で出てきたプログラムの解説を行います。

// 変数の初期化
var yosanArray = [
     {namae:"野球部", yosan:20000}
    ,{namae:"サッカー部", yosan:9000}
    ,{namae:"卓球部", yosan:12000}
    ,{namae:"写真部", yosan:15000}
];

// ソート
yosanArray.sort(
    function (a, b) {
        var aYosan = a["yosan"];
        var bYosan = b["yosan"];
        if (aYosan < bYosan) return -1;
        if (aYosan > bYosan) return 1;
        return 0;
    }
);

// 一覧表示
for (var i = 0; i < yosanArray.length; i ++) {
    document.write(
          i + " : "
        + yosanArray[i]["namae"]
        + ","
        + yosanArray[i]["yosan"] + "<br>"
    );
}

このプログラムでは、まず最初に、配列を作成しています。この配列の中には、各要素としてオブジェクトが格納されています。このオブジェクトの要素は、『yosanArray[0]["namae"]』あるいは『yosanArray[0].namae』のようにして参照できます(この場合の値は『野球部』)。

次に、この『yosanArray』オブジェクトの『sort』メソッドの引数に『無名関数』を入れて、ソートを行います。

この『無名関数』は、呼び出されるたびに、引数『a』『b』という、比較用の値を受け取ってから処理を行います。この引数『a』『b』には、配列の要素がそれぞれ代入されています。『yosanArray』配列の要素は、『{namae:"野球部", yosan:20000}』といったオブジェクトなので、これらのオブジェクトが順次代入されて実行されます。

このソート部分では、引数のオブジェクトから、『aYosan = a["yosan"]』『bYosan = b["yosan"]』のようにして予算の値を取り出しています。なぜならば、『a』『b』の引数は、『yosanArray』の要素、つまりオブジェクト(『{namae:"野球部", yosan:20000}』のような値)だからです。

そして、『aYosan』『bYosan』の数値を比較してソートを実行しています。引数『a』の予算が小さい場合は戻り値を『-1』に、大きい場合は戻り値を『1』にしています。同じ場合は『0』です。この結果、予算が小さい要素ほど、前に並びます。

最後は一覧表示です。この部分では、『yosanArray』配列の各要素(オブジェクト)を文字列にして出力しています。『yosanArray[i]["namae"]』の部分では名前を、『yosanArray[i]["yosan"]』の部分では予算を取り出して、文字列として連結しています。

上記のプログラムのソートの部分は、単純な数値の比較なので、以下のように短くかこともできます。

// ソート
yosanArray.sort(function (a, b) {
    return a.yosan - b.yosan;
});

また、ソート用の配列の各要素を、オブジェクトではなく、配列で書くこともできます。

以下、例を挙げます。

var yosanArray = [
     ["野球部",     20000]
    ,["サッカー部", 9000]
    ,["卓球部",     12000]
    ,["写真部",     15000]
];

// ソート
// 引数『a』や『b』には『["野球部", 20000]』のような配列が設定される
// 予算の数値は、配列の2番目の要素なので『a[1]』『b[1]』で参照できる
yosanArray.sort(function (a, b) {return a[1] - b[1]});

// 一覧表示
for (var i = 0; i < yosanArray.length; i ++) {
    document.write(
          i + " : "
        + yosanArray[i][0]
        + ","
        + yosanArray[i][1] + "<br>"
    );
}

『a[1]』『b[1]』と、オブジェクトではなく、配列として引数を利用しています。この際、『a』『b』は、『yosanArray』の要素(この場合は配列)を表します。また、添え字の『1』は、配列の2番目の値(予算にあたる数字)を示します。

サンプルの入手

以下は、今回出てきたサンプルです。

ZIPでまとめてダウンロード (右クリックから保存してください)

sample1.html』(単純なソート)を表示

sample2.html』(オブジェクトを利用したソート)を表示

sample2b.html』(ソートを短く)を表示

sample3.html』(配列版)を表示

sample4.html』(偶数を前に、奇数を後ろにソート)を表示

sample5.html』(オブジェクトの変数に無名関数を代入)を表示

プログラムの中身を見たい場合は、それぞれのHTMLファイルをブラウザで開いたあと、右クリックをして『ソースの表示』を選択してください。

メモ帳で、ファイルの中身を見ることができます。