Skip to content

Commit 3351d3b

Browse files
authored
Support sorting of maps and boolean comparison fns. (#711)
Hi, could you please review fix for `sort` to support maps and boolean comparator fns. This completes #708. I've also made some changes to the comments of the affected fns to indicate that we are now using the `compare` fn as default, but feel free to correct. Tests for the same were also added. Thanks --------- Co-authored-by: ikappaki <[email protected]>
1 parent 403bff7 commit 3351d3b

File tree

4 files changed

+69
-13
lines changed

4 files changed

+69
-13
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
* Fix issue with relative paths dropping their first character on MS-Windows (#703).
1010
* Fix incompatibility with `(str nil)` returning "nil" (#706).
1111
* Fix `sort-by` support for maps and boolean comparator fns (#709).
12+
* Fix `sort` support for maps and boolean comparator fns (#711).
1213

1314
## [v0.1.0a2]
1415
### Added

src/basilisp/core.lpy

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,11 +1158,9 @@
11581158
(basilisp.lang.runtime/compare x y))
11591159

11601160
(defn sort
1161-
"Return a sorted sequence of the elements from ``coll``\\.
1161+
"Return a sorted sequence of the elements from ``coll`` using the ``cmp`` comparator\\.
11621162

1163-
Unlike in Clojure, this function does not use :lpy:fn:`compare` directly. Instead,
1164-
the heuristics that ``compare`` uses to produce three-way comparator return values
1165-
are used to guide sorting."
1163+
The :lpy:fn:`compare` fn is used in if ``cmp`` is not provided."
11661164
([coll]
11671165
(basilisp.lang.runtime/sort coll))
11681166
([cmp coll]
@@ -3226,7 +3224,9 @@
32263224
(rest args)))
32273225

32283226
(defn sort-by
3229-
"Return a sorted sequence of the elements from ``coll``\\."
3227+
"Return a sorted sequence of the elements from ``coll`` using the ``cmp`` comparator on (``keyfn`` item)\\.
3228+
3229+
The :lpy:fn:`compare` fn is used in if ``cmp`` is not provided."
32303230
([keyfn coll]
32313231
(basilisp.lang.runtime/sort-by keyfn coll))
32323232
([keyfn cmp coll]

src/basilisp/lang/runtime.py

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,14 +1373,6 @@ def _compare_sets(x: IPersistentSet, y) -> int:
13731373
)
13741374

13751375

1376-
def sort(coll, f=None) -> Optional[ISeq]:
1377-
"""Return a sorted sequence of the elements in coll. If a comparator
1378-
function f is provided, compare elements in coll using f."""
1379-
if isinstance(coll, IPersistentMap):
1380-
coll = lseq.to_seq(coll)
1381-
return lseq.sequence(sorted(coll, key=Maybe(f).map(functools.cmp_to_key).value))
1382-
1383-
13841376
def _fn_to_comparator(f):
13851377
"""Coerce F comparator fn to a 3 way comparator fn."""
13861378

@@ -1401,6 +1393,43 @@ def cmp(x, y):
14011393
return cmp
14021394

14031395

1396+
def sort(coll, f=compare) -> Optional[ISeq]:
1397+
"""Return a sorted sequence of the elements in coll. If a
1398+
comparator function f is provided, compare elements in coll
1399+
using f or use the `compare` fn if not.
1400+
1401+
The comparator fn can be either a boolean or 3-way comparison fn."""
1402+
if isinstance(coll, IPersistentMap):
1403+
coll = lseq.to_seq(coll)
1404+
1405+
comparator = _fn_to_comparator(f)
1406+
1407+
class key:
1408+
__slots__ = ("obj",)
1409+
1410+
def __init__(self, obj):
1411+
self.obj = obj
1412+
1413+
def __lt__(self, other):
1414+
return comparator(self.obj, other.obj) < 0
1415+
1416+
def __gt__(self, other):
1417+
return comparator(self.obj, other.obj) > 0
1418+
1419+
def __eq__(self, other):
1420+
return comparator(self.obj, other.obj) == 0
1421+
1422+
def __le__(self, other):
1423+
return comparator(self.obj, other.obj) <= 0
1424+
1425+
def __ge__(self, other):
1426+
return comparator(self.obj, other.obj) >= 0
1427+
1428+
__hash__ = None # type: ignore
1429+
1430+
return lseq.sequence(sorted(coll, key=key))
1431+
1432+
14041433
def sort_by(keyfn, coll, cmp=compare) -> Optional[ISeq]:
14051434
"""Return a sorted sequence of the elements in coll. If a
14061435
comparator function cmp is provided, compare elements in coll

tests/basilisp/test_core_fns.lpy

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,32 @@
970970
(is (= "dsd" (min-key count "asd" "bsd" "dsd" "long word")))
971971
(is (= "a" (min-key count "a" "bsd" "dsd" "long word"))))
972972

973+
(deftest sort-test
974+
(testing "no cmp function"
975+
(is (= '() (sort [])))
976+
(is (= '(1 2 3 4 5) (sort [5 3 4 1 2])))
977+
(is (= '([:a] [5 5] [1 2 3])
978+
(sort [[1 2 3] [:a] [5 5]]))))
979+
980+
(testing "with cmp function"
981+
(let [cmp (fn [v1 v2] (< v2 v1))]
982+
(is (= '() (sort cmp [])))
983+
(is (= '([1 2 3] [5 5] [:a])
984+
(sort cmp [[1 2 3] [:a] [5 5]]))))
985+
986+
;; taken from clojuredocs
987+
(is (= '(:d :c :b :a) (sort #(compare %2 %1) '(:a :b :c :d)))))
988+
989+
(testing "sorting vectors"
990+
(are [res v] (= res (sort v))
991+
'([1] [3] [1 2]) [[1] [1 2] [3]]
992+
'([1] [1 2] [1 3]) [[1 3] [1] [1 2]]
993+
'([1] [0 1] [0 1]) [[0 1] [1] [0 1] ]))
994+
995+
(testing "sorting maps"
996+
(are [res v] (= res (sort v))
997+
'([:3 18] [:5 28] [:9 23]) {:9 23 :3 18 :5 28})))
998+
973999
(deftest sort-by-test
9741000
(testing "no cmp function"
9751001
(is (= '() (sort-by count [])))

0 commit comments

Comments
 (0)