【高階関数】sortメソッドについて解説 | GASおじさんのブログ
GASの基本

【高階関数】sortメソッドについて解説

sort GASの基本

本記事ではsortメソッドについて解説していきます。

YouTubeでも解説していますので動画で見たい方は以下からどうぞ。

sortメソッドの概要

sortメソッドは、配列の要素を並べ替えるメソッドです。

たとえば以下は、配列numbersの要素を昇順に並べ替えます。

function myFunction() {
  const numbers = [3, 5, 1, 4, 2];
  numbers.sort(function (a, b) {
    return a - b;
  });
  console.log(numbers);
}

コールバックをアロー関数で書き直すと以下のようになります。

function myFunction() {
  const numbers = [3, 5, 1, 4, 2];
  numbers.sort((a, b) => a - b);
  console.log(numbers);
}

こちらの実行結果は以下の通り。

最初numbersは[ 3, 5, 1, 4, 2 ]でしたが、ソート後は[ 1, 2, 3, 4, 5 ]になりました。

これを昇順ではなく降順にしたい場合は、以下のように書きます。

function myFunction() {
  const numbers = [3, 5, 1, 4, 2];
  numbers.sort((a, b) => b - a);
  console.log(numbers);
}

コールバックの返り値をb - aに書き換えました。こちらを実行すると以下のように降順になります。

sortメソッドの特徴

コールバックの引数は2つ与える

前回までに紹介したforEach, map, filterの3つはよく似た書き方をしていましたが、今回のsortはちょっと異色ですね。

配列numbersに対してforEach, map, filterを使う場合は、以下のような記述になることが多いと思います。

numbers.forEach(number => {});
numbers.map(number => {});
numbers.filter(number => {});

これらのメソッドのコールバックの引数には「number」という、配列numbersの単数系を記述することが多くなります。

一方sortを使う場合は、以下のようにコールバックに(a, b)という2つの引数を与える必要があります。

numbers.sort((a, b) => {});

引数名は(a, b)でなければならないというわけではなく、(number1, number2)とかでもいいのですが、慣習的に(a, b)とすることが多いです。

元の配列を変更する破壊的なメソッド

前回までに解説してきたforEach, map, filterはすべて、元の配列を変更しないメソッドでしたが、今回のsortメソッドは元の配列に変更を加えます。

たとえば以下のmapメソッドについて考えます。

function myFunction() {
  const numbers = [3, 5, 1, 4, 2];
  const newNumbers = numbers.map(number => number + 1);
}
  • 元の配列: numbers = [3, 5, 1, 4, 2]
  • 新しい配列: newNumbers = [4, 6, 2, 5, 3]

このとき、元の配列numbersは、mapメソッドを使った後も[3, 5, 1, 4, 2]という内容が保たれます。

一方、以下のsortメソッドの場合は…

function myFunction() {
  const numbers = [3, 5, 1, 4, 2];
  numbers.sort((a, b) => a - b);
}

こちらを実行すると元の配列numbersは、[1, 2, 3, 4, 5]という値に書き変わってしまいます。

このように、元の配列に変更を加えるメソッドのことを「破壊的なメソッド」と呼びます。

もし元の配列に変更を加えたくない場合は、例えば以下のように、元の配列のコピーを作ってから、そのコピーに対してsortメソッドを使う必要があります。

function myFunction() {
  const numbers = [3, 5, 1, 4, 2];
  const newNumbers = [...numbers]; // 元の配列のコピーを作る
  newNumbers.sort((a, b) => b - a); // コピーに対してsortを使う
}

これであれば元の配列numbersは[3, 5, 1, 4, 2]という内容を保持したまま、並べ替え後の配列[1, 2, 3, 4, 5]をnewNumbersとして取得することができます。

もしsortメソッドがなかったら…

もしsortメソッドがなかったら、たとえば以下のように書かないといけません。

function myFunction() {
  const numbers = [3, 5, 1, 4, 2];
  for (let i = 0; i < numbers.length - 1; i++) {
    for (let j = 0; j < numbers.length - 1 - i; j++) {
      if (numbers[j] > numbers[j + 1]) {
        [numbers[j], numbers[j + 1]] = [numbers[j + 1], numbers[j]];
      }
    }
  }
  console.log(numbers);
}

これは「バブルソート」というソートアルゴリズムを使った並べ替えです。

ソートアルゴリズムは他にも「クイックソート」や「セレクションソート」などいろいろなものがありますが、Google Apps Script(V8エンジン)では、sortメソッドの実装として Timsort が使用されているようです。

ソートアルゴリズムについては難しいので、ここでの詳細な解説は控えます。興味のある人は他のどこかで勉強してみてください。難しいですが、おもしろいですよ。

sortメソッドの使用例

たとえば、以下のようなオブジェクトの配列usersがあったとき、sortを使って年齢順に並べ替えることができます。

function myFunction() {
  const users = [
    { name: '佐藤', age: 25 },
    { name: '鈴木', age: 18 },
    { name: '高橋', age: 32 },
    { name: '田中', age: 20 },
    { name: '伊藤', age: 34 },
  ];
  users.sort((a, b) => a.age - b.age);
  console.log(users);
}

こちらの関数を実行すると、配列usersは以下のように並べ替えられます。

[ { name: '鈴木', age: 18 },
  { name: '田中', age: 20 },
  { name: '佐藤', age: 25 },
  { name: '高橋', age: 32 },
  { name: '伊藤', age: 34 } ]

したがって、一番若い人を取得したいときなんかは以下のように書けますね。

function getYoungestUser() {
  const users = [
    { name: '佐藤', age: 25 },
    { name: '鈴木', age: 18 },
    { name: '高橋', age: 32 },
    { name: '田中', age: 20 },
    { name: '伊藤', age: 34 },
  ];
  users.sort((a, b) => a.age - b.age);
  return users[0];
}

逆に一番年上の人を取得したい時は、a.ageb.ageを入れ替えて、以下のように書けます。

function getOldestUser() {
  const users = [
    { name: '佐藤', age: 25 },
    { name: '鈴木', age: 18 },
    { name: '高橋', age: 32 },
    { name: '田中', age: 20 },
    { name: '伊藤', age: 34 },
  ];
  users.sort((a, b) => b.age - a.age);
  return users[0];
}

まとめ

以上、sortメソッドについて解説しました。

sortメソッドは、前回までに紹介したforEach, map, filterメソッドと比較すると大きく異なる動きをします。

これらのメソッドの違いに注目しながら観察すると理解が深まりやすいので、以下のようにいろいろ書き比べながら、実際に手元で動かしてみて、勉強を進めてみてください。

const numbers = [3, 5, 1, 4, 2];

// forEach: 単純なループ
numbers.forEach(number => {
  console.log(number);
});

// map: 各要素を2乗してsquaredNumbersを生成
const squaredNumbers = numbers.map(number => number ** 2);

// filter: 2以上の数値を抽出したfilteredNumbersを生成
const filteredNumbers = numbers.filter(number => number >= 2);

// sort: 要素を昇順に並べ替え
numbers.sort((a, b) => a - b);

また、ソートアルゴリズムの勉強はとてもおもしろいので、興味のある人はぜひYoutubeやUdemyでソートアルゴリズムに関する動画を見つけてみてください。

1個おすすめの動画を貼っておきますね。

連載目次: 高階関数シリーズ

  1. 【GASの関数定義】関数宣言・関数式・アロー関数
  2. 【高階関数】forEachメソッドについて解説
  3. 【高階関数】mapメソッドについて解説
  4. 【高階関数】filterメソッドについて解説
  5. 【高階関数】sortメソッドについて解説

コメント

タイトルとURLをコピーしました