できない.dev

配列をソートするには

配列(リスト)を昇順・降順やキー指定で並べ替える基本形を各言語で示す。
破壊的か非破壊的か、JavaScript の既定が辞書順になる罠までを扱う。

公開: 更新:

各言語見出しの横のバッジは検証状態を表す。実行確認済みはコードを実際に実行して確認したもの、静的確認は構文と公式 API ドキュメントで確認したものである。

Python 実行確認済み

nums = [3, 1, 10, 2]
print(sorted(nums))            # 新しいリストを返す → [1, 2, 3, 10]
nums.sort(reverse=True)        # 元のリストをその場で降順に
words = ["banana", "apple"]
print(sorted(words, key=len))  # キー関数で基準を指定 → ['apple', 'banana']

sorted は新しいリストを返し、list.sort は元のリストをその場で並べ替える。
並べ替えの基準を変えたいときは key に関数を渡し、降順にするなら reverse=True を指定する。

JavaScript 実行確認済み

const nums = [3, 1, 10, 2];
console.log([...nums].sort((a, b) => a - b)); // 数値順 → [1, 2, 3, 10]
console.log(nums.sort());                     // 既定は文字列順 → [1, 10, 2, 3]

Array.prototype.sort は比較関数を渡さないと要素を文字列に変換して比較するため、数値はそのままだと辞書順になる。
数値順にするには (a, b) => a - b を渡す。
sort は破壊的なので、元の配列を残すならスプレッドでコピーしてから並べ替える。

TypeScript 実行確認済み

const nums: number[] = [3, 1, 10, 2];
const asc: number[] = [...nums].sort((a, b) => a - b);
console.log(asc); // [1, 2, 3, 10]

使い方は JavaScript と同じで、比較関数の引数と戻り値に型が付くだけである。
sort は元の配列を破壊的に並べ替えるため、number[] を保ちたいならスプレッドでコピーしてから呼ぶ。

Go 静的確認

package main
 
import (
	"fmt"
	"sort"
)
 
func main() {
	nums := []int{3, 1, 10, 2}
	sort.Ints(nums) // 昇順(破壊的)→ [1 2 3 10]
	fmt.Println(nums)
	sort.Slice(nums, func(i, j int) bool { return nums[i] > nums[j] }) // 任意の基準で降順
	fmt.Println(nums)
}

sort.Ints や sort.Strings は基本型のスライスをその場で昇順に並べ替える。
独自の基準や降順にするときは sort.Slice に比較関数を渡す。
いずれも元のスライスを破壊的に変更する。

Rust 静的確認

fn main() {
    let mut nums = vec![3, 1, 10, 2];
    nums.sort();                   // 破壊的に昇順 → [1, 2, 3, 10]
    nums.sort_by(|a, b| b.cmp(a)); // 比較関数で降順
    println!("{:?}", nums);
}

Vec::sort は要素をその場で昇順に並べ替える破壊的メソッドである。
降順や独自基準にするときは sort_by に比較クロージャを渡す。
元の順序を残したいなら clone してから並べ替える。

Java 実行確認済み

import java.util.*;
 
public class Main {
    public static void main(String[] args) {
        int[] nums = {3, 1, 10, 2};
        Arrays.sort(nums);                    // 配列を破壊的に昇順
        List<Integer> list = new ArrayList<>(List.of(3, 1, 10, 2));
        list.sort(Comparator.reverseOrder()); // List を降順
        System.out.println(Arrays.toString(nums) + " " + list);
    }
}

配列は Arrays.sort、List は Collections.sort か list.sort でその場で昇順に並べ替える。
降順や独自基準にするときは Comparator を渡す。
プリミティブの int[] は Comparator を使えないため、降順にするなら Integer[] にするか後で反転する。

C# 静的確認

using System;
using System.Linq;
 
class Program {
    static void Main() {
        int[] nums = {3, 1, 10, 2};
        Array.Sort(nums);                                    // 破壊的に昇順
        var desc = nums.OrderByDescending(x => x).ToArray();  // LINQ は非破壊
        Console.WriteLine(string.Join(",", desc));
    }
}

Array.Sort は配列をその場で昇順に並べ替える破壊的メソッドである。
元の配列を保ちたいときは LINQ の OrderBy / OrderByDescending を使うと新しい列を返す。
キー指定も OrderBy(x => x.Field) で書ける。

つまずき

JavaScript の Array.prototype.sort は、比較関数を渡さないと要素を文字列に変換してから並べ替える。
そのため [3, 1, 10, 2] を既定の sort で並べ替えると、数値順の [1, 2, 3, 10] ではなく辞書順の [1, 10, 2, 3] になるという定番の罠がある。
数値を並べ替えるときは必ず (a, b) => a - b のような比較関数を渡す。

破壊的か非破壊的か

並べ替えには、元の配列をその場で変更する破壊的なもの(JavaScript の sort、Python の list.sort、Go の sort.Ints)と、新しい列を返す非破壊的なもの(Python の sorted)がある。
元の順序を後でも使うなら、破壊的な関数を呼ぶ前にコピーしておくと安全である。

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