Skip to content

Commit 0f0145d

Browse files
committed
Merge pull request #79 from mperry/issue4
Fixes #4 where TreeMap.split is broken
2 parents 13791c8 + 2abd6bb commit 0f0145d

4 files changed

Lines changed: 164 additions & 3 deletions

File tree

core/src/main/java/fj/Equal.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,10 @@ public static <A> Equal<Set<A>> setEqual(final Equal<A> e) {
522522
return equal(curry((Set<A> a, Set<A> b) -> streamEqual(e).eq(a.toStream(), b.toStream())));
523523
}
524524

525+
public static <K, V> Equal<TreeMap<K, V>> treeMapEqual(Equal<K> k, Equal<V> v) {
526+
return equal(t1 -> t2 -> Equal.streamEqual(p2Equal(k, v)).eq(t1.toStream(), t2.toStream()));
527+
}
528+
525529
public static <A, B> Equal<Writer<A, B>> writerEqual(Equal<A> eq1, Equal<B> eq2) {
526530
return equal(w1 -> w2 -> p2Equal(eq1, eq2).eq(w1.run(), w2.run()));
527531
}

core/src/main/java/fj/data/Set.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,4 +474,18 @@ public static <A> Set<A> set(final Ord<A> o, final A ... as) {
474474
return s;
475475
}
476476

477+
/**
478+
* Constructs a set from the given elements.
479+
*
480+
* @param o An order for the elements of the new set.
481+
* @param list The elements to add to a set.
482+
* @return A new set containing the elements of the given list.
483+
*/
484+
public static <A> Set<A> set(final Ord<A> o, List<A> list) {
485+
Set<A> s = empty(o);
486+
for (final A a : list)
487+
s = s.insert(a);
488+
return s;
489+
}
490+
477491
}

core/src/main/java/fj/data/TreeMap.java

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,32 @@ public static <K, V> TreeMap<K, V> empty(final Ord<K> keyOrd) {
4040
return new TreeMap<K, V>(Set.empty(TreeMap.<K, Option<V>>ord(keyOrd)));
4141
}
4242

43+
/**
44+
* Constructs a tree map from the given elements.
45+
*
46+
* @param keyOrd An order for the keys of the tree map.
47+
* @param p2s The elements to construct the tree map with.
48+
* @return a TreeMap with the given elements.
49+
*/
50+
public static <K, V> TreeMap<K, V> treeMap(final Ord<K> keyOrd, final P2<K, V>... p2s) {
51+
return treeMap(keyOrd, List.list(p2s));
52+
}
53+
54+
/**
55+
* Constructs a tree map from the given elements.
56+
*
57+
* @param keyOrd An order for the keys of the tree map.
58+
* @param list The elements to construct the tree map with.
59+
* @return a TreeMap with the given elements.
60+
*/
61+
public static <K, V> TreeMap<K, V> treeMap(final Ord<K> keyOrd, final List<P2<K, V>> list) {
62+
TreeMap<K, V> tm = empty(keyOrd);
63+
for (final P2<K, V> p2 : list) {
64+
tm = tm.set(p2._1(), p2._2());
65+
}
66+
return tm;
67+
}
68+
4369
/**
4470
* Returns a potential value that the given key maps to.
4571
*
@@ -143,6 +169,9 @@ public Map<K, V> toMutableMap() {
143169
return m;
144170
}
145171

172+
public Stream<P2<K, V>> toStream() {
173+
return Stream.iteratorStream(iterator());
174+
}
146175
/**
147176
* An immutable projection of the given mutable map.
148177
*
@@ -208,13 +237,50 @@ public TreeMap<K, V> update(final K k, final F<V, V> f, final V v) {
208237
* key in this map, all the elements in the second set are mapped to keys greater than the given key,
209238
* and the optional value is the value associated with the given key if present, otherwise None.
210239
*/
211-
public P3<Set<V>, Option<V>, Set<V>> split(final K k) {
212-
final F<Set<P2<K, Option<V>>>, Set<V>> getSome = F1Functions.mapSet(F1Functions.o(Option.<V>fromSome(), P2.<K, Option<V>>__2())
213-
, tree.ord().comap(F1Functions.o(P.<K, Option<V>>p2().f(k), Option.<V>some_())));
240+
public P3<Set<V>, Option<V>, Set<V>> split(Ord<V> ord, final K k) {
241+
final F<Set<P2<K, Option<V>>>, Set<V>> getSome = F1Functions.mapSet(F1Functions.o(Option.<V>fromSome(), P2.<K, Option<V>>__2()), ord);
214242
return tree.split(p(k, Option.<V>none())).map1(getSome).map3(getSome)
215243
.map2(F1Functions.o(Option.<V>join(), F1Functions.mapOption(P2.<K, Option<V>>__2())));
216244
}
217245

246+
/**
247+
* Internal construction of a TreeMap from the given set.
248+
* @param ord An order for the keys of the tree map.
249+
* @param s The elements to construct the tree map with.
250+
* @return a TreeMap with the given elements.
251+
*/
252+
private static <K, V> TreeMap<K, V> treeMap(Ord<K> ord, Set<P2<K, Option<V>>> s) {
253+
TreeMap<K, V> empty = TreeMap.<K, V>empty(ord);
254+
TreeMap<K, V> tree = s.toList().foldLeft((tm, p2) -> {
255+
Option<V> opt = p2._2();
256+
if (opt.isSome()) {
257+
return tm.set(p2._1(), opt.some());
258+
}
259+
return tm;
260+
}, empty);
261+
return tree;
262+
}
263+
264+
/**
265+
* Splits this TreeMap at the given key. Returns a triple of:
266+
* <ul>
267+
* <li>A tree map containing all the values of this map associated with keys less than the given key.</li>
268+
* <li>An option of a value mapped to the given key, if it exists in this map, otherwise None.
269+
* <li>A tree map containing all the values of this map associated with keys greater than the given key.</li>
270+
* </ul>
271+
*
272+
* @param k A key at which to split this map.
273+
* @return Two tree maps and an optional value, where all keys in the first tree map are mapped
274+
* to keys less than the given key in this map, all the keys in the second tree map are mapped
275+
* to keys greater than the given key, and the optional value is the value associated with the
276+
* given key if present, otherwise None.
277+
*/
278+
public P3<TreeMap<K, V>, Option<V>, TreeMap<K, V>> splitLookup(final K k) {
279+
P3<Set<P2<K, Option<V>>>, Option<P2<K, Option<V>>>, Set<P2<K, Option<V>>>> p3 = tree.split(P.p(k, get(k)));
280+
Ord<K> o = tree.ord().<K>comap(k2 -> P.p(k2, Option.<V>none()));
281+
return P.p(treeMap(o, p3._1()), get(k), treeMap(o, p3._3()));
282+
}
283+
218284
/**
219285
* Maps the given function across the values of this TreeMap.
220286
*
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package fj.data;
2+
3+
import fj.Equal;
4+
import fj.Ord;
5+
import fj.P3;
6+
import fj.Show;
7+
import fj.P;
8+
import org.junit.Test;
9+
10+
import static fj.data.Option.some;
11+
import static org.junit.Assert.assertTrue;
12+
13+
/**
14+
* Created by MarkPerry on 11/01/2015.
15+
*/
16+
public class TreeMapTest {
17+
18+
@Test
19+
public void split() {
20+
// do the split
21+
int pivot = 4;
22+
int max = 5;
23+
List<Integer> l = List.range(1, max + 1);
24+
TreeMap<Integer, String> m2 = TreeMap.treeMap(Ord.intOrd, l.zip(l.map(i -> i.toString())));
25+
P3<Set<String>, Option<String>, Set<String>> p = m2.split(Ord.stringOrd, pivot);
26+
27+
// print debug info
28+
Show<TreeMap<Integer, String>> st = Show.treeMapShow(Show.intShow, Show.stringShow);
29+
Show<Set<String>> ss = Show.setShow(Show.stringShow);
30+
Show<Option<String>> so = Show.optionShow(Show.stringShow);
31+
Show<P3<Set<String>, Option<String>, Set<String>>> sp3 = Show.p3Show(ss, so, ss);
32+
if (true) {
33+
st.println(m2);
34+
sp3.println(p);
35+
}
36+
37+
// assert equals
38+
Equal<Set<String>> seq = Equal.setEqual(Equal.stringEqual);
39+
Set<String> left = toSetString(List.range(1, pivot));
40+
Set<String> right = toSetString(List.range(pivot + 1, max + 1));
41+
P3<Set<String>, Option<String>, Set<String>> expected = P.p(left, some(Integer.toString(pivot)), right);
42+
assertTrue(Equal.p3Equal(seq, Equal.optionEqual(Equal.stringEqual), seq).eq(p, expected));
43+
}
44+
45+
private static Set<String> toSetString(List<Integer> list) {
46+
return Set.set(Ord.stringOrd, list.map(i -> i.toString()));
47+
}
48+
49+
@Test
50+
public void splitLookup() {
51+
// do the split
52+
int pivot = 4;
53+
int max = 5;
54+
List<Integer> l = List.range(1, max + 1);
55+
TreeMap<Integer, String> m2 = TreeMap.treeMap(Ord.intOrd, l.zip(l.map(i -> i.toString())));
56+
P3<TreeMap<Integer, String>, Option<String>, TreeMap<Integer, String>> p3 = m2.splitLookup(pivot);
57+
58+
// create expected output
59+
List<Integer> leftList = List.range(1, pivot);
60+
TreeMap<Integer, String> leftMap = TreeMap.treeMap(Ord.intOrd, leftList.zip(leftList.map(i -> i.toString())));
61+
List<Integer> rightList = List.range(pivot + 1, max + 1);
62+
TreeMap<Integer, String> rightMap = TreeMap.treeMap(Ord.intOrd, rightList.zip(rightList.map(i -> i.toString())));
63+
64+
// debug info
65+
if (true) {
66+
Show<TreeMap<Integer, String>> st = Show.treeMapShow(Show.intShow, Show.stringShow);
67+
Show<P3<TreeMap<Integer, String>, Option<String>, TreeMap<Integer, String>>> sp3 = Show.p3Show(st, Show.optionShow(Show.stringShow), st);
68+
sp3.println(p3);
69+
}
70+
71+
// do the assert
72+
Equal<TreeMap<Integer, String>> tme = Equal.treeMapEqual(Equal.intEqual, Equal.stringEqual);
73+
Equal<P3<TreeMap<Integer, String>, Option<String>, TreeMap<Integer, String>>> eq = Equal.p3Equal(tme, Equal.optionEqual(Equal.stringEqual), tme);
74+
assertTrue(eq.eq(p3, P.p(leftMap, some(Integer.toString(pivot)), rightMap)));
75+
}
76+
77+
}

0 commit comments

Comments
 (0)