Skip to content

Commit cf75b64

Browse files
authored
Map conj supports other maps (#163)
* Map conj supports other maps * Actual merge function
1 parent e378f2c commit cf75b64

File tree

4 files changed

+44
-15
lines changed

4 files changed

+44
-15
lines changed

basilisp/core/__init__.lpy

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,3 +928,12 @@
928928
(lazy-seq (cons (f) (repeatedly f))))
929929
([n f]
930930
(lazy-seq (cons (f) (repeatedly (dec n) f)))))
931+
932+
(defn merge
933+
"Merge maps together from left to right as by conj. If a duplicate key
934+
appears in a map, the rightmost map's value for that key will be taken."
935+
[& maps]
936+
(when (some identity maps)
937+
(reduce #(conj %1 %2)
938+
{}
939+
maps)))

basilisp/lang/map.py

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -153,24 +153,22 @@ def update_with(self, merge_fn, *maps) -> "Map":
153153
m: PMap = self._inner.update_with(merge_fn, *maps)
154154
return Map(m)
155155

156-
def _cons(self, *entries) -> "Map":
156+
def cons(self, *elems) -> "Map":
157+
e = self._inner.evolver()
157158
try:
158-
e = self._inner.evolver()
159-
for entry in entries:
160-
e.set(entry.key, entry.value)
159+
for elem in elems:
160+
if isinstance(elem, Map):
161+
for entry in elem:
162+
e.set(entry.key, entry.value)
163+
elif isinstance(elem, MapEntry):
164+
e.set(elem.key, elem.value)
165+
else:
166+
entry = MapEntry.from_vec(elem)
167+
e.set(entry.key, entry.value)
161168
return Map(e.persistent(), meta=self.meta)
162-
except AttributeError:
169+
except (TypeError, ValueError):
163170
raise ValueError(
164-
"Argument to map conj must be castable to MapEntry")
165-
166-
def cons(self, *entries) -> "Map":
167-
try:
168-
e = self._inner.evolver()
169-
for entry in entries:
170-
e.set(entry.key, entry.value)
171-
return Map(e.persistent(), meta=self.meta)
172-
except AttributeError:
173-
return self._cons(*seq(entries).map(MapEntry.from_vec))
171+
"Argument to map conj must be another Map or castable to MapEntry")
174172

175173
@staticmethod
176174
def empty() -> "Map":

tests/core_test.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,3 +596,12 @@ def test_not_any__Q__():
596596
assert False is core.not_any__Q__(core.odd__Q__, vec.v(2, 3, 5, 7, 9))
597597
assert False is core.not_any__Q__(core.odd__Q__, vec.v(3, 5, 2, 7, 9))
598598
assert True is core.not_any__Q__(core.odd__Q__, vec.v(2, 4, 6, 8, 10))
599+
600+
601+
def test_merge():
602+
assert None is core.merge()
603+
assert lmap.Map.empty() == core.merge(lmap.Map.empty())
604+
assert lmap.map({kw.keyword("a"): 1}) == core.merge(lmap.map({kw.keyword("a"): 1}))
605+
assert lmap.map({kw.keyword("a"): 53, kw.keyword("b"): "hi"}) == core.merge(
606+
lmap.map({kw.keyword("a"): 1, kw.keyword("b"): "hi"}),
607+
lmap.map({kw.keyword("a"): 53}))

tests/map_test.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,19 @@ def test_entry():
6868

6969

7070
def test_map_cons():
71+
meta = lmap.m(tag="async")
72+
m1 = lmap.map({"first": "Chris"}, meta=meta)
73+
m2 = m1.cons({"last", "Cronk"})
74+
assert m1 is not m2
75+
assert m1 != m2
76+
assert len(m2) == 2
77+
assert meta == m1.meta
78+
assert meta == m2.meta
79+
assert "Chris" == m1.get("first")
80+
assert not m1.contains("last")
81+
assert "Cronk" == m2.get("last")
82+
assert "Chris" == m2.get("first")
83+
7184
meta = lmap.m(tag="async")
7285
m1 = lmap.map({"first": "Chris"}, meta=meta)
7386
m2 = m1.cons(MapEntry.of("last", "Cronk"))

0 commit comments

Comments
 (0)