Rebol3 Code Examplex
Natural sorting
The sorting of text that does more than rely on the order of individual characters codes to make the finding of individual strings easier for a human reader.
Rebol [
title: "Rosetta code: Natural sorting"
file: %Natural_sorting.r3
url: https://rosettacode.org/wiki/Natural_sorting
]
natural-sort: function/with [
"Naturally sort a block of strings"
items [block!]
][
sort/compare items :comparator
][
replacement: charset keys-of char-map: #[
#"IJ" "IJ" #"ij" "ij"
#"Œ" "OE" #"œ" "oe"
#"Æ" "AE" #"æ" "ae"
#"fi" "fi" #"fl" "fl"
#"À" #"a" #"Á" #"a" #"Â" #"a" #"Ã" #"a" #"Ä" #"a" #"Å" #"a"
#"à" #"a" #"á" #"a" #"â" #"a" #"ã" #"a" #"ä" #"a" #"å" #"a"
#"È" #"e" #"É" #"e" #"Ê" #"e" #"Ë" #"e"
#"è" #"e" #"é" #"e" #"ê" #"e" #"ë" #"e"
#"Ì" #"i" #"Í" #"i" #"Î" #"i" #"Ï" #"i"
#"ì" #"i" #"í" #"i" #"î" #"i" #"ï" #"i"
#"Ò" #"o" #"Ó" #"o" #"Ô" #"o" #"Õ" #"o" #"Ö" #"o" #"Ø" #"o"
#"ò" #"o" #"ó" #"o" #"ô" #"o" #"õ" #"o" #"ö" #"o" #"ø" #"o"
#"Ù" #"u" #"Ú" #"u" #"Û" #"u" #"Ü" #"u"
#"ù" #"u" #"ú" #"u" #"û" #"u" #"ü" #"u"
#"Ý" #"y" #"ý" #"y" #"Ÿ" #"y" #"ÿ" #"y"
#"Ñ" #"n" #"ñ" #"n"
#"Ç" #"c" #"ç" #"c"
#"Ð" #"d" #"ð" #"d"
#"Þ" "th" #"þ" "th"
#"ß" "ss" #"ſ" #"s" #"ʒ" #"s"
]
space: charset [" ^-^/^M" #"^A" - #"^K"]
normal: complement union space replacement
digits: charset [#"0" - #"9"]
not-digits: complement digits
sort-key: func [
"Generate natural sort key for string"
string [string!]
/local out s e
][
out: clear ""
parse string [
;; Trim leading whitespace
any space
;; If the first word is in common-leaders ("a", "an", "the"), drop it
opt [["an" | "a" | "the"] space]
collect into out any [
keep some normal
;; Collapse all internal whitespace runs to a single space
| some space keep (#" ")
;; For each character, apply de-accent/de-ligature/replacement via char-map
| some [
set c: replacement keep (char-map/:c)
]
]
]
;; Split the result into alternating string/integer segments
parse trim/tail out [collect any [
s: some digits e: keep (to integer! copy/part s e)
| end
| keep some not-digits
]]
]
comparator: func [a b] [
(sort-key a) < (sort-key b)
]
]
test-data: [
"# Ignoring leading spaces"
["ignore leading spaces: 2-2"
" ignore leading spaces: 2-1"
" ignore leading spaces: 2+0"
" ignore leading spaces: 2+1"]
"# Ignoring multiple adjacent spaces (m.a.s)"
["ignore m.a.s spaces: 2-2"
"ignore m.a.s spaces: 2-1"
"ignore m.a.s spaces: 2+0"
"ignore m.a.s spaces: 2+1"]
"# Equivalent whitespace characters"
["Equiv. spaces: 3-3"
"Equiv.^Mspaces: 3-2"
"Equiv.^Aspaces: 3-1"
"Equiv.^Kspaces: 3+0"
"Equiv.^/spaces: 3+1"
"Equiv.^-spaces: 3+2"]
"# Case Indepenent sort"
["cASE INDEPENENT: 3-2" "caSE INDEPENENT: 3-1" "casE INDEPENENT: 3+0" "case INDEPENENT: 3+1"]
"# Numeric fields as numerics"
["foo100bar99baz0.txt" "foo100bar10baz0.txt" "foo1000bar99baz10.txt" "foo1000bar99baz9.txt"]
"# Title sorts"
["The Wind in the Willows" "The 40th step more" "The 39 steps" "Wanda"]
"# Equivalent accented characters (and case)"
["Equiv. ý accents: 2-2" "Equiv. Ý accents: 2-1" "Equiv. y accents: 2+0" "Equiv. Y accents: 2+1"]
"# Character replacements"
["Start with an ʒ: 2-2" "Start with an ſ: 2-1" "Start with an ß: 2+0" "Start with an s: 2+1"]
]
foreach [title data] test-data [
print as-yellow title
prin "Text strings: "
probe new-line/all data on
prin "Naturally sorted: " probe natural-sort copy data
print ""
]Output:
# Ignoring leading spaces
Text strings: [
"ignore leading spaces: 2-2"
" ignore leading spaces: 2-1"
" ignore leading spaces: 2+0"
" ignore leading spaces: 2+1"
]
Naturally sorted: [
" ignore leading spaces: 2+0"
" ignore leading spaces: 2+1"
" ignore leading spaces: 2-1"
"ignore leading spaces: 2-2"
]
# Ignoring multiple adjacent spaces (m.a.s)
Text strings: [
"ignore m.a.s spaces: 2-2"
"ignore m.a.s spaces: 2-1"
"ignore m.a.s spaces: 2+0"
"ignore m.a.s spaces: 2+1"
]
Naturally sorted: [
"ignore m.a.s spaces: 2+0"
"ignore m.a.s spaces: 2+1"
"ignore m.a.s spaces: 2-1"
"ignore m.a.s spaces: 2-2"
]
# Equivalent whitespace characters
Text strings: [
"Equiv. spaces: 3-3"
"Equiv.^Mspaces: 3-2"
"Equiv.^Aspaces: 3-1"
"Equiv.^Kspaces: 3+0"
"Equiv.^/spaces: 3+1"
"Equiv.^-spaces: 3+2"
]
Naturally sorted: [
"Equiv.^Kspaces: 3+0"
"Equiv.^/spaces: 3+1"
"Equiv.^-spaces: 3+2"
"Equiv.^Aspaces: 3-1"
"Equiv.^Mspaces: 3-2"
"Equiv. spaces: 3-3"
]
# Case Indepenent sort
Text strings: [
"cASE INDEPENENT: 3-2"
"caSE INDEPENENT: 3-1"
"casE INDEPENENT: 3+0"
"case INDEPENENT: 3+1"
]
Naturally sorted: [
"casE INDEPENENT: 3+0"
"case INDEPENENT: 3+1"
"caSE INDEPENENT: 3-1"
"cASE INDEPENENT: 3-2"
]
# Numeric fields as numerics
Text strings: [
"foo100bar99baz0.txt"
"foo100bar10baz0.txt"
"foo1000bar99baz10.txt"
"foo1000bar99baz9.txt"
]
Naturally sorted: [
"foo100bar10baz0.txt"
"foo100bar99baz0.txt"
"foo1000bar99baz9.txt"
"foo1000bar99baz10.txt"
]
# Title sorts
Text strings: [
"The Wind in the Willows"
"The 40th step more"
"The 39 steps"
"Wanda"
]
Naturally sorted: [
"The 39 steps"
"The 40th step more"
"Wanda"
"The Wind in the Willows"
]
# Equivalent accented characters (and case)
Text strings: [
"Equiv. ý accents: 2-2"
"Equiv. Ý accents: 2-1"
"Equiv. y accents: 2+0"
"Equiv. Y accents: 2+1"
]
Naturally sorted: [
"Equiv. y accents: 2+0"
"Equiv. Y accents: 2+1"
"Equiv. Ý accents: 2-1"
"Equiv. ý accents: 2-2"
]
# Character replacements
Text strings: [
"Start with an ʒ: 2-2"
"Start with an ſ: 2-1"
"Start with an ß: 2+0"
"Start with an s: 2+1"
]
Naturally sorted: [
"Start with an s: 2+1"
"Start with an ſ: 2-1"
"Start with an ʒ: 2-2"
"Start with an ß: 2+0"
]