Every language
12 lenguajes, copy-ready. One at a time with syntax highlighting, or all inline.
JSJavaScript
// copy first — sort MUTATES the receiver:
const sorted = [...items].sort((a, b) => a.name.localeCompare(b.name));
// machine keys, or when locale rules are wrong for the data:
const byTitle = [...items].sort((a, b) => (a.title < b.title ? -1 : a.title > b.title ? 1 : 0));
// multi-key tiebreak: 0 (equal) falls through to the next comparator
const byTitleThenDate = [...items].sort(
(a, b) => a.title.localeCompare(b.title) || new Date(a.date) - new Date(b.date),
);Array.prototype.sort MUTATES — spread into a new array first (or use toSorted(), ES2023) or the input order is silently gone. localeCompare for human-facing strings (it handles accents and locale rules); the < / > ? -1 : 1 form for machine keys. The || chain is the tiebreak idiom: only 0 falls through to the next key.
TSTypeScript
interface Item {
title: string;
date: string; // ISO — strings compare chronologically
}
const byTitle = (a: Item, b: Item): number => a.title.localeCompare(b.title);
// multi-key: compose comparators with || — zero (equal) falls through
const byTitleThenDate = (a: Item, b: Item): number =>
byTitle(a, b) || b.date.localeCompare(a.date); // newest first on tiesThe typed comparator (a: Item, b: Item) => number is the whole contract — return a negative for less, 0 for equal, positive for greater; any magnitude works. Keep comparators as named pure functions: they compose with || (0 is falsy), unit-test trivially, and reuse across sort call sites.
GoGo
import "sort"
// fast — but NOT stable: equal-Title rows may arrive in any order
sort.Slice(items, func(i, j int) bool {
return items[i].Title < items[j].Title
})
// equal-key rows keep their input order:
sort.SliceStable(items, func(i, j int) bool {
return items[i].Title < items[j].Title
})sort.Slice is NOT stable — sort.SliceStable is the one that preserves input order for equal keys; pick deliberately, not by habit. The contract is a less-than bool, not a three-way int. Go 1.21+ adds the generic slices.SortFunc / slices.SortStableFunc (slices package) as the modern API — same semantics.
RsRust
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct Item {
title: String,
}
// stable — equal-key rows keep their input order
items.sort_by(|a, b| a.title.cmp(&b.title));
// multi-key: tuples compare field-by-field, the free tiebreak chain
items.sort_by(|a, b| (&a.last, &a.first).cmp(&(&b.last, &b.first)));
// faster, no stability guarantee — fine when keys are unique
items.sort_unstable_by(|a, b| a.title.cmp(&b.title));sort_by is stable; sort_unstable_by is faster — choose on purpose. Deriving PartialOrd/Ord on the struct gives the free a < b ordering; a.title.cmp(&b.title) returns the three-way Ordering (Less/Equal/Greater), and tuple comparison is the multi-key idiom with no chaining needed.
PHPPHP
// <=> (spaceship) is the comparator primitive: -1, 0, or 1, no branches
usort($items, fn($a, $b) => $a['title'] <=> $b['title']);
// multi-key: compare fields left to right until one differs
usort($items, fn($a, $b) => [$a['last'], $a['first']] <=> [$b['last'], $b['first']]);
// usort re-indexes numerically — make the reset explicit, not accidental:
$items = array_values($items);usort resets the keys: the result is re-indexed 0..n-1 and any string keys are lost — array_values() documents that intent. uasort preserves keys, uksort sorts by them. PHP's sort is stable since PHP 8.0 (pre-8.0 it was not — a real upgrade trap for equal-key rows).
PyPython
sorted_items = sorted(items, key=lambda x: x['title']) # new list
# multi-key via tuple — ties break on the second field:
by_last_first = sorted(items, key=lambda x: (x['last'], x['first']))
# descending: reverse=True, not negation (only works on numbers)
by_title_desc = sorted(items, key=lambda x: x['title'], reverse=True)
# items.sort(key=...) sorts in place insteadTimsort is stable — equal-key rows keep input order — and sorted() returns a new list while list.sort() mutates. The tuple key (x['last'], x['first']) is the multi-key idiom. reverse=True beats key negation (impossible on strings); for mixed directions use two passes, sorting by the LAST key first — stability makes it work.
C#C#
// LINQ — lazy, stable, never mutates the source:
var sorted = items.OrderBy(i => i.Title)
.ThenBy(i => i.Date);
// in place, on List<T> — UNSTABLE:
items.Sort((a, b) => string.CompareOrdinal(a.Title, b.Title));List<T>.Sort is UNSTABLE — equal-key rows can reorder; LINQ's OrderBy is documented stable and never mutates the source, but it is lazy — nothing sorts until enumeration. string.CompareOrdinal for machine keys; string.Compare(strA, strB, cultureInfo) when the sort is human-facing (accents, locale rules).
JvJava
import java.util.Comparator;
items.sort(Comparator.comparing(Item::title)
.thenComparing(Item::date));
// nullable field — decide where the nulls land, or get an NPE mid-sort:
items.sort(Comparator.comparing(
Item::title,
Comparator.nullsLast(Comparator.naturalOrder())));Comparator.comparing(...).thenComparing(...) replaced the old anonymous-class comparators — the chain reads in sort order and each stage has a Descending twin. List.sort and Stream.sorted run TimSort: stable. comparing with a second argument is where nullsLast/nullsFirst goes; without it a null key throws NullPointerException during the sort.
SwSwift
let sorted = items.sorted { $0.title < $1.title }
// explicit multi-key tiebreak:
let full = items.sorted {
$0.title == $1.title ? $0.date < $1.date : $0.title < $1.title
}
// mutating form, on a var array:
var mutable = items
mutable.sort { $0.title < $1.title }sorted(by:) returns a new array; sort() mutates in place. The stdlib documents the sort as guaranteed stable (an adaptive merge sort), but an explicit tiebreak chain still reads clearer than leaning on it — tuples ($0.title, $0.date) < ($1.title, $1.date) also compare lexicographically. Conform the element to Comparable and the closure-free sorted() comes free.
KtKotlin
val sorted = items.sortedBy { it.title } // RETURNS a new list
// multi-key, mixed directions:
val full = items.sortedWith(
compareBy<Item> { it.title }.thenByDescending { it.date })
// in place, on a MutableList — returns Unit, not a list:
items.sortBy { it.title }The trap is the two spellings: sortedBy/sortedWith RETURN a new list; sortBy/sortWith MUTATE in place and return Unit — assigning the result of sortBy compiles to Unit and silently loses the sorted reference. compareBy({ it.title }, { it.date }) is the multi-key vararg form. Uses the same stable TimSort as Java.
RbRuby
# key block — the key is computed once per element:
sorted = items.sort_by { |i| i[:title] }
# array keys compare element-wise — the multi-key form:
by_last_first = items.sort_by { |i| [i[:last], i[:first]] }
# two-arg comparator block, when you need full control (descending on ties):
items.sort { |a, b| [a[:title], b[:date]] <=> [b[:title], a[:date]] }sort_by takes a KEY block (decorate-sort-undecorate — the key runs once per element); sort takes a two-arg comparator block on <=>, the three-way operator. Both return a new array (sort! / sort_by! mutate in place). MRI's sort is stable in practice but the spec does not promise it — add an explicit tiebreak when equal-key order matters.
ZigZig
const std = @import("std");
const Item = struct { title: []const u8 };
fn byTitle(_: void, a: Item, b: Item) bool {
return std.mem.lessThan(u8, a.title, b.title);
}
// stable; the context arg ({} here) is mandatory even when unused:
std.mem.sort(Item, items, {}, byTitle);
// when equal-key order does not matter and speed does:
std.sort.pdq(Item, items, {}, byTitle);The comparator is a comptime fn of (context, a, b) bool — less-than, not three-way — and the context parameter is mandatory even when you ignore it ({} for void). std.mem.sort is the stable block sort; std.sort.pdq / std.sort.insertion are the named unstable choices. lessThan is byte-wise: no collation, no case folding — normalize before sorting human strings.