Skip to content

Commit 14a21c6

Browse files
committed
Adding MagnetizeBy and Magnetize
1 parent 89ff37c commit 14a21c6

File tree

5 files changed

+189
-0
lines changed

5 files changed

+189
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
2121
- `Kleisli`, the abstract representation of a `Kleisli` arrow (`Monad#flatMap`) as an `Fn1`
2222
- `These`, a `CoProduct3` of `A`, `B`, or `Tuple2<A,B>`
2323
- `Span`, for splitting an `Iterable` into contiguous elements matching a predicate
24+
- `MagnetizeBy` and `Magnetize`, for grouping elements by pairwise predicate tests
2425

2526
### Deprecated
2627
- `Either#trying` in favor of `Try#trying`
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.jnape.palatable.lambda.functions.builtin.fn1;
2+
3+
import com.jnape.palatable.lambda.functions.Fn1;
4+
5+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Eq.eq;
6+
import static com.jnape.palatable.lambda.functions.builtin.fn2.MagnetizeBy.magnetizeBy;
7+
8+
/**
9+
* {@link Magnetize} an {@link Iterable} using value equality as the magnetizing function.
10+
*
11+
* @param <A> the Iterable element type
12+
*/
13+
public final class Magnetize<A> implements Fn1<Iterable<A>, Iterable<Iterable<A>>> {
14+
15+
private static final Magnetize INSTANCE = new Magnetize();
16+
17+
private Magnetize() {
18+
}
19+
20+
@Override
21+
public Iterable<Iterable<A>> apply(Iterable<A> as) {
22+
return magnetizeBy(eq().toBiFunction(), as);
23+
}
24+
25+
@SuppressWarnings("unchecked")
26+
public static <A> Magnetize<A> magnetize() {
27+
return INSTANCE;
28+
}
29+
30+
public static <A> Iterable<Iterable<A>> magnetize(Iterable<A> as) {
31+
return Magnetize.<A>magnetize().apply(as);
32+
}
33+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.jnape.palatable.lambda.functions.builtin.fn2;
2+
3+
import com.jnape.palatable.lambda.functions.Fn1;
4+
import com.jnape.palatable.lambda.functions.Fn2;
5+
6+
import java.util.Collections;
7+
import java.util.function.BiFunction;
8+
9+
import static com.jnape.palatable.lambda.adt.Maybe.just;
10+
import static com.jnape.palatable.lambda.adt.Maybe.nothing;
11+
import static com.jnape.palatable.lambda.adt.hlist.HList.tuple;
12+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Size.size;
13+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Uncons.uncons;
14+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Cons.cons;
15+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Drop.drop;
16+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Into.into;
17+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Unfoldr.unfoldr;
18+
19+
/**
20+
* Given a binary predicate and an <code>{@link Iterable}&lt;A&gt;</code>, return an <code>{@link Iterable}&lt;{@link
21+
* Iterable}&lt;A&gt;&gt;</code> of the contiguous groups of elements that match the predicate pairwise.
22+
* <p>
23+
* Example: <code>magnetizeBy((x, y) -> x <= y, asList(1, 2, 3, 2, 2, 3, 2, 1)); // [[1, 2, 3], [2, 2, 3], [2],
24+
* [1]]</code>
25+
*
26+
* @param <A>
27+
*/
28+
public final class MagnetizeBy<A> implements Fn2<BiFunction<? super A, ? super A, Boolean>, Iterable<A>, Iterable<Iterable<A>>> {
29+
30+
private static final MagnetizeBy INSTANCE = new MagnetizeBy();
31+
32+
private MagnetizeBy() {
33+
}
34+
35+
@Override
36+
public Iterable<Iterable<A>> apply(BiFunction<? super A, ? super A, Boolean> predicate, Iterable<A> as) {
37+
return () -> uncons(as).fmap(into((A head, Iterable<A> tail) -> {
38+
Iterable<A> group = cons(head, unfoldr(into((pivot, ys) -> uncons(ys)
39+
.flatMap(into((y, recurse) -> predicate.apply(pivot, y)
40+
? just(tuple(y, tuple(y, recurse)))
41+
: nothing()))), tuple(head, tail)));
42+
return cons(group, () -> apply(predicate, drop(size(group).intValue(), as)).iterator());
43+
})).orElseGet(() -> Collections::emptyIterator).iterator();
44+
}
45+
46+
@SuppressWarnings("unchecked")
47+
public static <A> MagnetizeBy<A> magnetizeBy() {
48+
return INSTANCE;
49+
}
50+
51+
public static <A> Fn1<Iterable<A>, Iterable<Iterable<A>>> magnetizeBy(
52+
BiFunction<? super A, ? super A, Boolean> predicate) {
53+
return MagnetizeBy.<A>magnetizeBy().apply(predicate);
54+
}
55+
56+
public static <A> Iterable<Iterable<A>> magnetizeBy(
57+
BiFunction<? super A, ? super A, Boolean> predicate,
58+
Iterable<A> as) {
59+
return MagnetizeBy.<A>magnetizeBy(predicate).apply(as);
60+
}
61+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.jnape.palatable.lambda.functions.builtin.fn1;
2+
3+
import com.jnape.palatable.lambda.functions.Fn1;
4+
import com.jnape.palatable.traitor.annotations.TestTraits;
5+
import com.jnape.palatable.traitor.runners.Traits;
6+
import org.junit.Test;
7+
import org.junit.runner.RunWith;
8+
import testsupport.traits.EmptyIterableSupport;
9+
import testsupport.traits.FiniteIteration;
10+
import testsupport.traits.ImmutableIteration;
11+
import testsupport.traits.InfiniteIterableSupport;
12+
import testsupport.traits.Laziness;
13+
14+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Magnetize.magnetize;
15+
import static java.util.Arrays.asList;
16+
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
17+
import static org.junit.Assert.assertThat;
18+
import static testsupport.matchers.IterableMatcher.iterates;
19+
20+
@RunWith(Traits.class)
21+
public class MagnetizeTest {
22+
23+
@TestTraits({EmptyIterableSupport.class, InfiniteIterableSupport.class, FiniteIteration.class, ImmutableIteration.class, Laziness.class})
24+
public Fn1<Iterable<Object>, Iterable<Iterable<Object>>> testSubject() {
25+
return magnetize();
26+
}
27+
28+
@Test
29+
@SuppressWarnings("unchecked")
30+
public void magnetizesElementsByPredicateOutcome() {
31+
assertThat(magnetize(asList(1, 1, 2, 3, 3, 3, 2, 2, 1)),
32+
contains(iterates(1, 1),
33+
iterates(2),
34+
iterates(3, 3, 3),
35+
iterates(2, 2),
36+
iterates(1)));
37+
}
38+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.jnape.palatable.lambda.functions.builtin.fn2;
2+
3+
import com.jnape.palatable.lambda.functions.Fn1;
4+
import com.jnape.palatable.traitor.annotations.TestTraits;
5+
import com.jnape.palatable.traitor.runners.Traits;
6+
import org.junit.Test;
7+
import org.junit.runner.RunWith;
8+
import testsupport.traits.EmptyIterableSupport;
9+
import testsupport.traits.FiniteIteration;
10+
import testsupport.traits.ImmutableIteration;
11+
import testsupport.traits.InfiniteIterableSupport;
12+
import testsupport.traits.Laziness;
13+
14+
import java.util.function.BiFunction;
15+
16+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Last.last;
17+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat;
18+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Eq.eq;
19+
import static com.jnape.palatable.lambda.functions.builtin.fn2.MagnetizeBy.magnetizeBy;
20+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Take.take;
21+
import static java.util.Arrays.asList;
22+
import static java.util.Collections.emptyList;
23+
import static java.util.Collections.singletonList;
24+
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
25+
import static org.junit.Assert.assertThat;
26+
import static testsupport.matchers.IterableMatcher.isEmpty;
27+
import static testsupport.matchers.IterableMatcher.iterates;
28+
29+
@RunWith(Traits.class)
30+
public class MagnetizeByTest {
31+
32+
@TestTraits({EmptyIterableSupport.class, InfiniteIterableSupport.class, FiniteIteration.class, ImmutableIteration.class, Laziness.class})
33+
public Fn1<Iterable<Object>, Iterable<Iterable<Object>>> testSubject() {
34+
return magnetizeBy(eq().toBiFunction());
35+
}
36+
37+
@Test
38+
@SuppressWarnings("unchecked")
39+
public void magnetizesElementsByPredicateOutcome() {
40+
BiFunction<Integer, Integer, Boolean> lte = (x, y) -> x <= y;
41+
assertThat(magnetizeBy(lte, emptyList()), isEmpty());
42+
assertThat(magnetizeBy(lte, singletonList(1)), contains(iterates(1)));
43+
assertThat(magnetizeBy(lte, asList(1, 2, 3, 2, 2, 3, 2, 1)),
44+
contains(iterates(1, 2, 3),
45+
iterates(2, 2, 3),
46+
iterates(2),
47+
iterates(1)));
48+
}
49+
50+
@Test
51+
public void stackSafety() {
52+
int stackBlowingNumber = 10_000;
53+
assertThat(last(magnetizeBy((x, y) -> false, take(stackBlowingNumber, repeat(1)))).orElseThrow(AssertionError::new),
54+
iterates(1));
55+
}
56+
}

0 commit comments

Comments
 (0)