Skip to content

Commit 58698b7

Browse files
authored
Introduce leftJoin on Seq (#2719)
1 parent 8852522 commit 58698b7

File tree

2 files changed

+77
-0
lines changed

2 files changed

+77
-0
lines changed

src/main/java/io/vavr/collection/Seq.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
* <li>{@link #combinations()}</li>
9090
* <li>{@link #combinations(int)}</li>
9191
* <li>{@link #intersperse(Object)}</li>
92+
* <li>{@link #leftJoin(Traversable, Function, Function)}</li>
9293
* <li>{@link #padTo(int, Object)}</li>
9394
* <li>{@link #permutations()}</li>
9495
* <li>{@link #reverse()}</li>
@@ -630,6 +631,33 @@ default Option<Integer> lastIndexWhereOption(Predicate<? super T> predicate, int
630631
return Collections.indexOption(lastIndexWhere(predicate, end));
631632
}
632633

634+
/**
635+
* Perform a left join operation, which returns all the elements of {@code this} matched with
636+
* an {@link Option}al element from the {@code right} {@link Traversable}. Matching is done
637+
* using keys that are extracted from each element with the given functions. If the keys are
638+
* not unique, only the first element from {@code right} will be considered.
639+
*
640+
* @param right The {@link Traversable} to join with.
641+
* @param getKeyLeft Mapper from T to the key used for matching.
642+
* @param getKeyRight Mapper from U to the key used for matching.
643+
* @param <K> The key type.
644+
* @param <U> The type of the right {@link Traversable}.
645+
* @return A {@link Seq} of {@link Tuple2}s with T on the left and an {@link Option} element
646+
* on the right.
647+
*/
648+
default <K, U> Seq<Tuple2<T, Option<U>>> leftJoin(
649+
Traversable<U> right,
650+
Function<? super T, ? extends K> getKeyLeft,
651+
Function<? super U, ? extends K> getKeyRight) {
652+
Traversable<Tuple2<K, U>> rightWithKey =
653+
right.map(u -> Tuple.of(getKeyRight.apply(u), u));
654+
return this.map(t -> Tuple.of(getKeyLeft.apply(t), t))
655+
.map(lt -> Tuple.of(
656+
lt._2,
657+
rightWithKey.find(rt -> lt._1.equals(rt._1)).map(t1 -> t1._2)
658+
));
659+
}
660+
633661
/**
634662
* Turns this sequence into a plain function returning an Option result.
635663
*

src/test/java/io/vavr/collection/AbstractSeqTest.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2357,4 +2357,53 @@ enum OneEnum implements SomeInterface {
23572357
enum SecondEnum implements SomeInterface {
23582358
A1, A2, A3;
23592359
}
2360+
2361+
2362+
@Test
2363+
public void shouldLeftJoinNotIncludeRightKeysThatDontExistOnTheLeft() {
2364+
Seq<Integer> left = of(1, 2, 5);
2365+
Seq<String> right = of("1", "2", "6");
2366+
Seq<Tuple2<Integer, Option<String>>> joined =
2367+
left.leftJoin(right, Function.identity(), Integer::parseInt);
2368+
assertThat(joined).containsExactly(
2369+
Tuple.of(1, Option.of("1")),
2370+
Tuple.of(2, Option.of("2")),
2371+
Tuple.of(5, Option.none())
2372+
);
2373+
}
2374+
2375+
@Test
2376+
public void shouldLeftJoinOnlyIncludeTheFirstMatchingKeyFromTheRight() {
2377+
Seq<Integer> left = of(1, 2, 5);
2378+
Seq<String> right = of("1", "2", "55", "5");
2379+
Seq<Tuple2<Integer, Option<String>>> joined =
2380+
left.leftJoin(right, Function.identity(), s -> Integer.parseInt(s.substring(0, 1)));
2381+
assertThat(joined).containsExactly(
2382+
Tuple.of(1, Option.of("1")),
2383+
Tuple.of(2, Option.of("2")),
2384+
Tuple.of(5, Option.of("55"))
2385+
);
2386+
}
2387+
2388+
@Test
2389+
public void shouldLeftJoinNotHaveAnyRightElementsWhenRightIsEmpty() {
2390+
Seq<Integer> left = of(1, 2, 5);
2391+
Seq<String> right = empty();
2392+
Seq<Tuple2<Integer, Option<String>>> joined =
2393+
left.leftJoin(right, Function.identity(), s -> Integer.parseInt(s.substring(0, 1)));
2394+
assertThat(joined).containsExactly(
2395+
Tuple.of(1, Option.none()),
2396+
Tuple.of(2, Option.none()),
2397+
Tuple.of(5, Option.none())
2398+
);
2399+
}
2400+
2401+
@Test
2402+
public void shouldLeftJoinReturnEmptySeqWhenLeftIsEmpty() {
2403+
Seq<Integer> left = empty();
2404+
Seq<String> right = of("1", "2", "6");
2405+
Seq<Tuple2<Integer, Option<String>>> joined =
2406+
left.leftJoin(right, Function.identity(), Integer::parseInt);
2407+
assertThat(joined).isEmpty();
2408+
}
23602409
}

0 commit comments

Comments
 (0)