JavaScript

レシピ

配列

配列の要素をソートする

配列の要素を並び替える方法について解説します。

目次

配列の要素を並び替える

配列の要素を並び替えるときはsortメソッドを使います。

要素を並び替えるとき、まずはどのような基準で並び替えるか決める必要があります。
今回は以下の並び替えを解説していきます。

  • 数値の小さい順・大きい順
  • アルファベット順
  • あいうえお順(ひらがな)
  • アイウエオ順(アイウエオ)

まずはそれぞれの並び替えを行い、最後に数値・アルファベット・ひらがななどが混在した文字の並び替えを行っていきます。

数値のソート

配列の要素が数値のときに、数値の小さい順(昇順)、または大きい順(降順)で並び替えを行います。

以下の例では数値が入った配列に対して値の小さい順に並び替えるために、sortメソッドをそのまま実行したパターン1と、処理を指定して実行するパターン2をそれぞれ実行しています。

コード例

var numbers = [ 4, 0, 447, 32, 3, 612, 6, -1, 1, 14, -9, 11, -59];

// パターン1:そのまま実行
numbers.sort();
console.log(numbers);
// (13) [-1, -59, -9, 0, 1, 11, 14, 3, 32, 4, 447, 6, 612]

// パターン2:処理を指定して実行
numbers.sort(function(a,b){
  if(a > b) {
    return 1;
  }
  if(a < b) {
    return -1;
  }
  return 0;
});
console.log(numbers);
// (13) [-59, -9, -1, 0, 1, 3, 4, 6, 11, 14, 32, 447, 612]

パターン2は期待した通りのソートがされますが、パターン1のsortメソッドをそのまま実行したときは期待していない結果になりました。

これはsortメソッドの標準のソート処理が間違いっているわけではなく、ソートするときの比較が数値ではなく文字列のUnicodeのコードポイントを参照することが原因です。
例えば、「-1」と「-59」であれば数値としては「-59」の方が小さいですが、Unicodeのコードポイントを確認すると「-1」の方が値が小さくなります。

以下のコードでは負の値の前につく「」と数字に対して、Unicodeのコードポイントを取得するcharCodeAtメソッドを実行します。

コード例

console.log("-".charCodeAt()); // 45
console.log("1".charCodeAt()); // 49
console.log("5".charCodeAt()); // 53
console.log("9".charCodeAt()); // 57

出力された値を確認すると、「」が一番小さい値になり、続いて数字の順に値が増えていくことが分かります。
この値を配列の各値に当てはめると、「-1」は「45,49」、「-59」は「45,53,57」、「-9」は「45,57」になります。

そして、sortメソッドは数値の1文字目から順にコードポイントを確認してソートを行うため、「-1」と「59」では1文字目の「」は同じですが、2文字目の「1」と「5」では「49」と「53」で「-1」の方が小さくなり、「-53」より前に配置されます。

-53」と「-9」の比較においても、1文字目は同じですが2文字目の「5」は「53」、「9」は「57」で「-53」の方が小さいという判定になり、「-53」の方が「-9」より前に配置されます。

以上のように文字列として比較を行うため桁数は「1文字目、2文字目」という解釈になり、「4」と「447」の比較についても2文字目がない「4」が前に配置される結果になります。

ソート処理を指定するパターン2については、変数abに比較する値を渡して、Unicodeのコードポイントではなく数値として比較しています。
aの方が大きい場合は「1」を返してaよりbを前に配置、小さい場合は「-1」を返してbよりaを前に配置、等しい場合は「0」を返して並び替えをしないという内容です。
この処理を要素に対して繰り返していき、配列の値を小さい順に並び替えることができます。

ソートに指定する処理を少し変更すると、配列の値を大きい順に並び替えることもできます。

コード例

var numbers = [ 4, 0, 447, 32, 3, 612, 6, -1, 1, 14, -9, 11, -59];

numbers.sort(function(a,b){
  if(a > b) {
    return -1;
  }
  if(a < b) {
    return 1;
  }
  return 0;
});
console.log(numbers);
// (13) [612, 447, 32, 14, 11, 6, 4, 3, 1, 0, -1, -9, -59]

2つの値を比較したときの「a > b」の戻り値を「1」から「-1」に変更し、続く「a < b」を「-1」から「1」に変更しています。
この変更で数値の小さい順(昇順)から大きい順(降順)に変更したソートを行うことができます。

アルファベットのソート

配列の要素がアルファベットのときに、a〜zのアルファベット順、または反対順に並び替えを行います。

以下の例ではパターン1ではsortメソッドをそのまま使い、パターン2はIntl.Collatorオブジェクトcompareメソッドを使ってソートします。

コード例

var alphabets = [ 'lion', 'rhinos', 'cat', 'Fox', 'Panda', 'dog', 'Dolphin', 'fish'];

// パターン1
alphabets.sort();
console.log(alphabets);
// (8) ["Dolphin", "Fox", "Panda", "cat", "dog", "fish", "lion", "rhinos"]


// パターン2
var collator = new Intl.Collator('en');
alphabets.sort(collator.compare);
console.log(alphabets);
// (8) ["cat", "dog", "Dolphin", "fish", "Fox", "lion", "Panda", "rhinos"]

パターン1ではアルファベットの大文字/小文字を区別してソートしています。
これはUnicodeのコードポイントにおいて、大文字の方が小さい番号が割り振られているためです。
試しにアルファベットの「A」「B」「Z」の大文字、小文字のコードポイントを出力すると次のようになります。

コード例

console.log("A".charCodeAt()); // 65
console.log("B".charCodeAt()); // 66
console.log("Z".charCodeAt()); // 90
console.log("a".charCodeAt()); // 97
console.log("b".charCodeAt()); // 98
console.log("z".charCodeAt()); // 122

大文字の「A」が「65」から始まって「Z」の「90」まで続き、その後に小文字の「a」から「z」まで「97」〜「122」と続いていることが分かります。
sortメソッドをそのまま使うと、1文字目からこの順番にソートされるため、大文字と小文字で別々にアルファベット順に並び替えることができます。

続いてパターン2のソート結果をみてみると、こちらは大文字と小文字の区別をせずにアルファベット順にソートしていることが分かります。

Intl.Collatorオブジェクトは「en」や「ja」などの言語コードを指定することで、特定の言語に基づいた文字の比較を行うことができるオブジェクトです。
今回はアルファベットなので「en」を指定してオブジェクトのインスタンスを作成して、変数collatorに入れます。

その後、sortメソッドでソート処理としてIntl.Collatorオブジェクトcompareメソッドを指定し、文字列の比較を行ってソートを実行します。

Intl.Collatorオブジェクトcompareメソッドは小文字と大文字の比較をすると、小文字が前に配置されます。
例えば仮に、以下のような配列をソートしてみると分かりやすいかもしれません。

コード例

var alphabets = [ 'A', 'a', 'aa', 'A', 'a', 'AA'];
var collator = new Intl.Collator('en');

alphabets.sort(collator.compare);
console.log(alphabets);
// (6) ["a", "a", "A", "A", "aa", "AA"]

1文字だけの「a」が前に配置され、続いて「A」、そして2文字の「aa」「AA」の順にソートされました。
この結果から、大文字よりは小文字が優先され、文字数は少ない方が前に配置されることが分かります。

以上のパターン1、パターン2のどちらを使うかは、大文字と小文字の区別をしてソートする必要がある場合はパターン1、区別せずにソートしたいときはパターン2を使います。

また、もし「a〜z」ではなく反対の「z〜a」の順番にしたいときは、一度ソートした配列に対してreverseメソッドを使うと簡単に並び替えることができます。

コード例

var alphabets = [ 'lion', 'rhinos', 'cat', 'Fox', 'Panda', 'dog', 'Dolphin', 'fish'];
		
// パターン1
alphabets.sort();
console.log(alphabets);
// (8) ["Dolphin", "Fox", "Panda", "cat", "dog", "fish", "lion", "rhinos"]

// パターン1を逆順にソート
console.log(alphabets.reverse());
// (8) ["rhinos", "lion", "fish", "dog", "cat", "Panda", "Fox", "Dolphin"]

// パターン2
var collator = new Intl.Collator('en');
alphabets.sort(collator.compare);
console.log(alphabets);
// (8) ["cat", "dog", "Dolphin", "fish", "Fox", "lion", "Panda", "rhinos"]

// パターン2を逆順にソート
console.log(alphabets.reverse());
// (8) ["rhinos", "Panda", "lion", "Fox", "fish", "Dolphin", "dog", "cat"]

ひらがなのソート

配列の要素がひらがなの文字列のときに、sortメソッドをそのまま使う形であいうえお順に並び替えることができます。

コード例

var hiraganas = [ 'あう', 'う', 'あお', 'え', 'あい', 'か', 'お', 'い', 'あえ', 'あ', 'ああ'];

hiraganas.sort();
console.log(hiraganas);
// (11) ["あ", "ああ", "あい", "あう", "あえ", "あお", "い", "う", "え", "お", "か"]

「ば」などの濁点や「ぱ」など半濁点のある文字の並び順は、濁点がない文字に続いて配置されます。
例えば濁点の「ば」や、半濁点の「ぱ」は、濁点の付かない「は」と「ひ」の間に配置されます。

コード例

var hiraganas = [ 'は', 'ま', 'ば', 'ひ', 'ぱ'];

hiraganas.sort();
console.log(hiraganas);
// (5) ["は", "ば", "ぱ", "ひ", "ま"]

「ゃ」などの小さい文字は通常の文字「や」などよりも前に配置されます。

コード例

var hiraganas = [ 'ゆ', 'よ', 'ょ', 'ゅ', 'や', 'ゃ'];

hiraganas.sort();
console.log(hiraganas);
// (6) ["ゃ", "や", "ゅ", "ゆ", "ょ", "よ"]

カタカナのソート

配列の要素がカタカナの文字列のときは、sortメソッドをそのまま使う形でアイウエオ順に並び替えることができます。

コード例

var katakanas = [ 'アイ', 'アエ', 'アオ', 'イ', 'ア', 'エ', 'アア', 'アウ'];

katakanas.sort();
console.log(katakanas);
// (8) ["ア", "アア", "アイ", "アウ", "アエ", "アオ", "イ", "エ"]

濁点、半濁点、小さい文字の並び順については上記の「ひらがな」と同様です。
例えば「ハ」であれば、次の並びになります。

コード例

var katakanas = [ 'パ', 'バ', 'ヒ', 'ハ'];

katakanas.sort();
console.log(katakanas);
// (3) ["ハ", "バ", "パ", "ヒ"]

「ァ」などの小さい文字は通常の文字よりも前に配置されます。

コード例

var katakanas = [ 'イ', 'ィ', 'ア', 'ウ', 'ァ', 'ゥ'];

katakanas.sort();
console.log(katakanas);
// (6) ["ァ", "ア", "ィ", "イ", "ゥ", "ウ"]

漢字のソート

配列の要素に漢字の文字列があるときは、sortメソッドでUnicodeのコードポイントでソートすることはできますが、読み方や漢数字の順番に並び替えることはできません。
以下の例は漢数字に対してsortメソッドを使用して出力しています。

コード例

var katakanas = [ 'アイ', 'アエ', 'アオ', 'イ', 'ア', 'エ', 'アア', 'アウ'];

katakanas.sort();
console.log(katakanas);
// (8) ["ア", "アア", "アイ", "アウ", "アエ", "アオ", "イ", "エ"]

濁点、半濁点、小さい文字の並び順については上記の「ひらがな」と同様です。
例えば「ハ」であれば、次の並びになります。

コード例

var kanjis = [ '一', '二', '三', '四', '五', '六', '七', '八', '九', '十'];

kanjis.sort();
console.log(kanjis);
// (10) ["一", "七", "三", "九", "二", "五", "八", "六", "十", "四"]

漢数字の順番が不規則になってしまったように見えますが、charCodeAtメソッドでUnicodeのコードポイントを出力してみると、この順番にソートされていることが分かります。

コード例

console.log("一".charCodeAt()); // 19968
console.log("七".charCodeAt()); // 19971
console.log("三".charCodeAt()); // 19977
console.log("九".charCodeAt()); // 20061
console.log("二".charCodeAt()); // 20108
console.log("五".charCodeAt()); // 20116
console.log("八".charCodeAt()); // 20843
console.log("六".charCodeAt()); // 20845
console.log("十".charCodeAt()); // 21313
console.log("四".charCodeAt()); // 22235

sortメソッドでは漢数字をソートすることと同様に、漢字の読み方でソートすることも非常に難しいです。
もし読み方で並び替えを行う場合は、ひらがなかカタカナの振り仮名とセットで用意するとうまくソートすることができます。

以下の例はパターン1は通常のsortメソッドでソートし、パターン2は振り仮名の入った多次元配列をソート、そしてパターン3はオブジェクト形式のソートをそれぞれ実行します。

コード例

// パターン1:通常の配列(読み方の順にソートできない)
var names = [ '宇野', '浅野', '渡辺', '太田', '斉藤', '伊藤', '江藤', '加藤'];
names.sort();
console.log(names);
// (8) ["伊藤", "加藤", "太田", "宇野", "斉藤", "江藤", "浅野", "渡辺"]


// パターン2:多次元配列
var names2 = [
  ['ウノ', '宇野'],
  ['アサノ', '浅野'],
  ['ワタナベ', '渡辺'],
  ['オオタ', '太田'],
  ['サイトウ', '斉藤'],
  ['イトウ', '伊藤'],
  ['エトウ', '江藤'],
  ['カトウ', '加藤']
];
names2.sort(function(a, b){
  if(a[0] > b[0]) {
    return 1;
  }
  if(a[0] < b[0]) {
    return -1;
  }
  return 0;
});
console.log(names2);
// 0: (2) ["アサノ", "浅野"]
// 1: (2) ["イトウ", "伊藤"]
// 2: (2) ["ウノ", "宇野"]
// 3: (2) ["エトウ", "江藤"]
// 4: (2) ["オオタ", "太田"]
// 5: (2) ["カトウ", "加藤"]
// 6: (2) ["サイトウ", "斉藤"]
// 7: (2) ["ワタナベ", "渡辺"]


// パターン3:オブジェクト形式
var names3 = [
  {
    id: 15001,
    name: '宇野',
    kana: 'ウノ',
  },
  {
    id: 15002,
    name: '浅野',
    kana: 'アサノ',
  },
  {
    id: 15003,
    name: '渡辺',
    kana: 'ワタナベ',
  },
  {
    id: 15004,
    name: '太田',
    kana: 'オオタ',
  },
  {
    id: 15005,
    name: '斉藤',
    kana: 'サイトウ',
  },
  {
    id: 15006,
    name: '伊藤',
    kana: 'イトウ',
  },
  {
    id: 15007,
    name: '江藤',
    kana: 'エトウ',
  },
  {
    id: 15008,
    name: '加藤',
    kana: 'カトウ',
  }
];
names3.sort(function(a, b){
  if(a.kana > b.kana) {
    return 1;
  }
  if(a.kana < b.kana) {
    return -1;
  }
  return 0;
});
console.log(names3);
// 0: {id: 15002, name: "浅野", kana: "アサノ"}
// 1: {id: 15006, name: "伊藤", kana: "イトウ"}
// 2: {id: 15001, name: "宇野", kana: "ウノ"}
// 3: {id: 15007, name: "江藤", kana: "エトウ"}
// 4: {id: 15004, name: "太田", kana: "オオタ"}
// 5: {id: 15008, name: "加藤", kana: "カトウ"}
// 6: {id: 15005, name: "斉藤", kana: "サイトウ"}
// 7: {id: 15003, name: "渡辺", kana: "ワタナベ"}

パターン1は漢字がUnicodeのコードポイントでそのままソートされるため読み方の順番にはソートされません。

続いてパターン2では振り仮名の入った多次元配列を用意することに加えて、sortメソッドに比較する処理を渡してソートしています。
この比較する処理においては振り仮名が入っている添字(インデックス)を指定して、漢字ではなくカタカナの振り仮名で比較を行っているところがポイントになります。

最後のパターン3では、オブジェクト形式でデータを用意して、パターン2と同様にsortメソッドに比較処理を渡してソートします。
こちらはオブジェクトなのでプロパティ「kana」で比較を行うことで、読み方のアイウエオ順にソートすることができます。

色々な値が混在する配列のソート

配列の要素に数字やアルファベット、日本語が混在する場合、sortメソッドでソートを実行するとそれぞれの値にまとめられた上で、要素が並び替えられます。

以下の例は変数chaosに入った配列に対して、パターン1はsortメソッドでそのままソート、パターン2はIntl.Collatorオブジェクトcompareメソッドでソートを実行します。

コード例

var chaos = [ '猫', 'ねずみ', '55', 'さかな', 'a', 4, 1, 'シマウマ', '虎', 'シーラカンス', '0', '蟹', 'シカ', 'C','ライオン', 'カラス', '5', 'Z', 'ダチョウ', 'z', 'c', 'いぬ', 'さい'];

// パターン1:そのまま実行
chaos.sort();
console.log(chaos);
// (23) ["0", 1, 4, "5", "55", "C", "Z", "a", "c", "z", "いぬ", "さい", "さかな", "ねずみ", "カラス", "シカ", "シマウマ", "シーラカンス", "ダチョウ", "ライオン", "猫", "虎", "蟹"]


// パターン2:Intl.Collatorオブジェクトのcompareメソッドでソート
var collator = new Intl.Collator('ja');
chaos.sort(collator.compare);
console.log(chaos);
// (23) ["0", 1, 4, "5", "55", "a", "c", "C", "z", "Z", "いぬ", "カラス", "さい", "さかな", "シーラカンス", "シカ", "シマウマ", "ダチョウ", "ねずみ", "ライオン", "蟹", "虎", "猫"]

2つのソート結果は似ていますが、アルファベットやひらがな・カタカナの並びに差異があることが分かります。

パターン1では値を以下のグループでまとめた上で、ソートを実行します。

  1. 数値
  2. アルファベット(大文字):A〜Z
  3. アルファベット(小文字):a〜z
  4. ひらがな:あいうえお順
  5. カタカナ:アイウエオ順
  6. 漢字

続いて、パターン2では以下のグループにまとめてソートを実行します。

  1. 数値
  2. アルファベット(大文字/小文字):A〜Z
  3. ひらがな/カタカナ:あいうえお順
  4. 漢字

アルファベットの大文字/小文字と、ひらがな/カタカナを分けてソートするかで大きな違いがあります。
アルファベットの大文字/小文字を区別せずにアルファベット順にソートしたい場合や、ひらがな/カタカナを区別せずにあいうえお順にソートしたいときは、パターン2のIntl.Collatorオブジェクトcompareメソッドを使ってソートすると便利です。

オブジェクトのソート

要素にオブジェクトが入っている配列をソートするときは、sortメソッドに比較処理を指定するときにプロパティを設定すると、プロパティの値でソートすることができます。

以下の例は変数usersに入った配列に対して、idプロパティの値が小さい順(昇順)にソートします。

コード例

var users = [
  {
    name: "John",
    id: 150002,
    regist_datetime: "2016/07/01 22:48:05"
  },
  {
    name: "Emilia",
    id: 150005,
    regist_datetime: "2016/07/02 10:03:24"
  },
  {
    name: "Taro",
    id: 150003,
    regist_datetime: "2016/07/02 08:12:10"
  },
  {
    name: "Camila",
    id: 150004,
    regist_datetime: "2016/07/02 08:53:32"
  },
  {
    name: "Mike",
    id: 150001,
    regist_datetime: "2016/07/01 14:00:00"
  }
];

// idプロパティの値でソート
users.sort(function(a,b){
  if(a.id > b.id) {
    return 1;
  }
  if(a.id < b.id) {
    return -1;
  }
  return 0;
});

console.log(users);
// 0: {name: "Mike", id: 150001, regist_datetime: "2016/07/01 14:00:00"}
// 1: {name: "John", id: 150002, regist_datetime: "2016/07/01 22:48:05"}
// 2: {name: "Taro", id: 150003, regist_datetime: "2016/07/02 08:12:10"}
// 3: {name: "Camila", id: 150004, regist_datetime: "2016/07/02 08:53:32"}
// 4: {name: "Emilia", id: 150005, regist_datetime: "2016/07/02 10:03:24"}

sortメソッドに渡した引数abには、比較するオブジェクトが渡されます。
そこで、比較処理で「a.id」のようにプロパティの値を参照することで、上記の例のようにID順に並び替えることができます。
名前のアルファベット順に並び替えたいときは、「if(a.id > b.id) {」を「if(a.name > b.name) {」のように変更します。

もし番号の小さい順(昇順)ではなく大きい順(降順)に並び替えるときは、以下の赤字の箇所のように比較する式の戻り値を変更してください。

コード例

var users = [
  {
    name: "John",
    id: 150002,
    regist_datetime: "2016/07/01 22:48:05"
  },
  {
    name: "Emilia",
    id: 150005,
    regist_datetime: "2016/07/02 10:03:24"
  },
  {
    name: "Taro",
    id: 150003,
    regist_datetime: "2016/07/02 08:12:10"
  },
  {
    name: "Camila",
    id: 150004,
    regist_datetime: "2016/07/02 08:53:32"
  },
  {
    name: "Mike",
    id: 150001,
    regist_datetime: "2016/07/01 14:00:00"
  }
];

users.sort(function(a,b){
  if(a.id > b.id) {
    return -1;
  }
  if(a.id < b.id) {
    return 1;
  }
  return 0;
});

console.log(users);
// 0: {name: "Emilia", id: 150005, regist_datetime: "2016/07/02 10:03:24"}
// 1: {name: "Camila", id: 150004, regist_datetime: "2016/07/02 08:53:32"}
// 2: {name: "Taro", id: 150003, regist_datetime: "2016/07/02 08:12:10"}
// 3: {name: "John", id: 150002, regist_datetime: "2016/07/01 22:48:05"}
// 4: {name: "Mike", id: 150001, regist_datetime: "2016/07/01 14:00:00"}

こちらの記事は役に立ちましたか?

ありがとうございます。
もしよろしければ、あわせてフィードバックや要望などをご入力ください。

コメントありがとうございます!
運営の参考にさせていただきます。