Skip to content

Commit 8f21657

Browse files
authored
update & group-by core functions (#298)
* update & group-by core functions * Fix select-keys test
1 parent d616451 commit 8f21657

File tree

4 files changed

+67
-8
lines changed

4 files changed

+67
-8
lines changed

src/basilisp/core/__init__.lpy

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,13 @@
730730
([m k default]
731731
(basilisp.lang.runtime/get m k default)))
732732

733+
(defn update
734+
"Updates the value for key k in associative data structure m with the return value
735+
from calling (f old-v & args). If m is nil, use an empty map. If k is not in m,
736+
old-v will be nil."
737+
[m k f & args]
738+
(apply basilisp.lang.runtime/update m k f args))
739+
733740
(defn key
734741
"Return the key from a map entry."
735742
[entry]
@@ -980,6 +987,21 @@
980987
[pred coll]
981988
[(take-while pred coll) (drop-while pred coll)])
982989

990+
(defn group-by
991+
"Return a map whose keys are the result of calling f on each element
992+
in coll and whose values are vectors of the values which produced the
993+
corresponding key, in the order they were added."
994+
[f coll]
995+
(if-not (seq coll)
996+
{}
997+
(reduce (fn [m v]
998+
(let [group (f v)]
999+
(if (contains? m group)
1000+
(update m group conj v)
1001+
(assoc m group [v]))))
1002+
{}
1003+
coll)))
1004+
9831005
(defn interpose
9841006
"Return a lazy sequence of elements of coll separated by sep. If
9851007
coll is empty, return an empty sequence."
@@ -1794,12 +1816,14 @@
17941816
(defn select-keys
17951817
"Return a map with only the keys of m which are in ks."
17961818
[m ks]
1797-
(reduce (fn [new-map k]
1798-
(if (contains? m k)
1799-
(assoc new-map k (get m k))
1800-
new-map))
1801-
{}
1802-
ks))
1819+
(if-not (seq ks)
1820+
{}
1821+
(reduce (fn [new-map k]
1822+
(if (contains? m k)
1823+
(assoc new-map k (get m k))
1824+
new-map))
1825+
{}
1826+
ks)))
18031827

18041828
;;;;;;;;;;;;;;;;;;;;;;;;;;;
18051829
;; Destructuring Support ;;
@@ -2097,4 +2121,3 @@
20972121
(mapcat destructure))]
20982122
`(let* [~@bindings]
20992123
~@body)))
2100-

src/basilisp/lang/runtime.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,19 @@ def assoc(m, *kvs):
613613
raise TypeError(f"Object of type {type(m)} does not implement Associative interface")
614614

615615

616+
def update(m, k, f, *args):
617+
"""Updates the value for key k in associative data structure m with the return value from
618+
calling f(old_v, *args). If m is None, use an empty map. If k is not in m, old_v will be
619+
None."""
620+
if m is None:
621+
return lmap.Map.empty().assoc(k, f(None, *args))
622+
if isinstance(m, lassoc.Associative):
623+
old_v = m.entry(k)
624+
new_v = f(old_v, *args)
625+
return m.assoc(k, new_v)
626+
raise TypeError(f"Object of type {type(m)} does not implement Associative interface")
627+
628+
616629
def conj(coll, *xs):
617630
"""Conjoin xs to collection. New elements may be added in different positions
618631
depending on the type of coll. conj returns the same type as coll. If coll

tests/basilisp/core_test.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,11 @@ def test_split_with():
719719
assert vec.v(llist.List.empty(), llist.l(2, 4, 6, 8)) == core.split_with(core.odd__Q__, vec.v(2, 4, 6, 8))
720720

721721

722+
def test_group_by():
723+
assert lmap.Map.empty() == core.group_by(core.inc, vec.Vector.empty())
724+
assert lmap.map({True: vec.v(1, 3), False: vec.v(2, 4)}) == core.group_by(core.odd__Q__, vec.v(1, 2, 3, 4))
725+
726+
722727
def test_interpose():
723728
assert llist.List.empty() == core.interpose(",", vec.Vector.empty())
724729
assert llist.l("hi") == core.interpose(",", vec.v("hi"))
@@ -813,7 +818,7 @@ def test_re_seq():
813818

814819
def test_select_keys():
815820
assert lmap.Map.empty() == core.select_keys(
816-
lmap.Map.empty(), vec.v(vec.Vector.empty()))
821+
lmap.Map.empty(), vec.Vector.empty())
817822
assert lmap.Map.empty() == core.select_keys(
818823
lmap.Map.empty(), vec.v(kw.keyword('a'), kw.keyword('b')))
819824
assert lmap.map({kw.keyword('a'): "a", kw.keyword('b'): "b"}) == core.select_keys(

tests/basilisp/runtime_test.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,24 @@ def test_assoc():
181181
runtime.assoc(llist.List.empty(), 1, "a")
182182

183183

184+
def test_update():
185+
assert lmap.map({"a": 1}) == runtime.update(None, "a", lambda _: 1)
186+
assert lmap.map({"a": 50}) == runtime.update(None, "a", lambda _, x: x, 50)
187+
188+
assert lmap.map({"a": 2}) == runtime.update(lmap.map({"a": 1}), "a", lambda x: x + 1)
189+
assert lmap.map({"a": 4}) == runtime.update(lmap.map({"a": 1}), "a", lambda x, y: x * y + 1, 3)
190+
191+
assert lmap.map({"a": 1, "b": "string"}) == runtime.update(lmap.map({"a": 1}), "b", lambda _: "string")
192+
assert lmap.map({"a": 1, "b": "string"}) == runtime.update(lmap.map({"a": 1, "b": 583}), "b", lambda _: "string")
193+
194+
assert vec.v("a") == runtime.update(vec.Vector.empty(), 0, lambda _: "a")
195+
assert vec.v("yay", "b") == runtime.update(vec.v("a", "b"), 0, lambda x: f"y{x}y")
196+
assert vec.v("a", "boy") == runtime.update(vec.v("a", "b"), 1, lambda x, y: f"{x}{y}", "oy")
197+
198+
with pytest.raises(TypeError):
199+
runtime.update(llist.List.empty(), 1, lambda _: "y")
200+
201+
184202
def test_conj():
185203
assert llist.l(1) == runtime.conj(None, 1)
186204
assert llist.l(3, 2, 1) == runtime.conj(None, 1, 2, 3)

0 commit comments

Comments
 (0)