Skip to content

Commit 096007f

Browse files
Add distinctByKeepLast for Seq (#3006)
1 parent 02bf617 commit 096007f

File tree

13 files changed

+217
-0
lines changed

13 files changed

+217
-0
lines changed

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

+12
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,18 @@ public <U> Array<T> distinctBy(Function<? super T, ? extends U> keyExtractor) {
746746
return filter(t -> seen.add(keyExtractor.apply(t)));
747747
}
748748

749+
@Override
750+
public Array<T> distinctByKeepLast(Comparator<? super T> comparator) {
751+
Objects.requireNonNull(comparator, "comparator is null");
752+
return ofAll(iterator().distinctByKeepLast(comparator));
753+
}
754+
755+
@Override
756+
public <U> Array<T> distinctByKeepLast(Function<? super T, ? extends U> keyExtractor) {
757+
Objects.requireNonNull(keyExtractor, "keyExtractor is null");
758+
return ofAll(iterator().distinctByKeepLast(keyExtractor));
759+
}
760+
749761
@Override
750762
public Array<T> drop(int n) {
751763
if (n <= 0) {

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

+12
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,18 @@ public <U> CharSeq distinctBy(Function<? super Character, ? extends U> keyExtrac
471471
return filter(t -> seen.add(keyExtractor.apply(t)));
472472
}
473473

474+
@Override
475+
public CharSeq distinctByKeepLast(Comparator<? super Character> comparator) {
476+
Objects.requireNonNull(comparator, "comparator is null");
477+
return ofAll(iterator().distinctByKeepLast(comparator));
478+
}
479+
480+
@Override
481+
public <U> CharSeq distinctByKeepLast(Function<? super Character, ? extends U> keyExtractor) {
482+
Objects.requireNonNull(keyExtractor, "keyExtractor is null");
483+
return ofAll(iterator().distinctByKeepLast(keyExtractor));
484+
}
485+
474486
@Override
475487
public CharSeq drop(int n) {
476488
if (n <= 0) {

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

+6
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ public boolean isDefinedAt(Integer index) {
105105
@Override
106106
<U> IndexedSeq<T> distinctBy(Function<? super T, ? extends U> keyExtractor);
107107

108+
@Override
109+
IndexedSeq<T> distinctByKeepLast(Comparator<? super T> comparator);
110+
111+
@Override
112+
<U> IndexedSeq<T> distinctByKeepLast(Function<? super T, ? extends U> keyExtractor);
113+
108114
@Override
109115
IndexedSeq<T> drop(int n);
110116

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

+24
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,30 @@ default <U> Iterator<T> distinctBy(Function<? super T, ? extends U> keyExtractor
13731373
}
13741374
}
13751375

1376+
default Iterator<T> distinctByKeepLast(Comparator<? super T> comparator) {
1377+
Objects.requireNonNull(comparator, "comparator is null");
1378+
if (!hasNext()) {
1379+
return empty();
1380+
} else {
1381+
return Collections.reverseIterator(new DistinctIterator<>(
1382+
Collections.reverseIterator(this),
1383+
TreeSet.empty(comparator),
1384+
Function.identity()));
1385+
}
1386+
}
1387+
1388+
default <U> Iterator<T> distinctByKeepLast(Function<? super T, ? extends U> keyExtractor) {
1389+
Objects.requireNonNull(keyExtractor, "keyExtractor is null");
1390+
if (!hasNext()) {
1391+
return empty();
1392+
} else {
1393+
return Collections.reverseIterator(new DistinctIterator<>(
1394+
Collections.reverseIterator(this),
1395+
io.vavr.collection.HashSet.empty(),
1396+
keyExtractor));
1397+
}
1398+
}
1399+
13761400
/**
13771401
* Removes up to n elements from this iterator.
13781402
*

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

+6
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ public boolean isDefinedAt(Integer index) {
105105
@Override
106106
<U> LinearSeq<T> distinctBy(Function<? super T, ? extends U> keyExtractor);
107107

108+
@Override
109+
LinearSeq<T> distinctByKeepLast(Comparator<? super T> comparator);
110+
111+
@Override
112+
<U> LinearSeq<T> distinctByKeepLast(Function<? super T, ? extends U> keyExtractor);
113+
108114
@Override
109115
LinearSeq<T> drop(int n);
110116

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

+12
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,18 @@ default <U> List<T> distinctBy(Function<? super T, ? extends U> keyExtractor) {
817817
return filter(t -> seen.add(keyExtractor.apply(t)));
818818
}
819819

820+
@Override
821+
default List<T> distinctByKeepLast(Comparator<? super T> comparator) {
822+
Objects.requireNonNull(comparator, "comparator is null");
823+
return ofAll(iterator().distinctByKeepLast(comparator));
824+
}
825+
826+
@Override
827+
default <U> List<T> distinctByKeepLast(Function<? super T, ? extends U> keyExtractor) {
828+
Objects.requireNonNull(keyExtractor, "keyExtractor is null");
829+
return ofAll(iterator().distinctByKeepLast(keyExtractor));
830+
}
831+
820832
@Override
821833
default List<T> drop(int n) {
822834
if (n <= 0) {

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

+12
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,18 @@ public <U> Queue<T> distinctBy(Function<? super T, ? extends U> keyExtractor) {
737737
return ofAll(toList().distinctBy(keyExtractor));
738738
}
739739

740+
@Override
741+
public Queue<T> distinctByKeepLast(Comparator<? super T> comparator) {
742+
Objects.requireNonNull(comparator, "comparator is null");
743+
return ofAll(toList().distinctByKeepLast(comparator));
744+
}
745+
746+
@Override
747+
public <U> Queue<T> distinctByKeepLast(Function<? super T, ? extends U> keyExtractor) {
748+
Objects.requireNonNull(keyExtractor, "keyExtractor is null");
749+
return ofAll(toList().distinctByKeepLast(keyExtractor));
750+
}
751+
740752
@Override
741753
public Queue<T> drop(int n) {
742754
if (n <= 0) {

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

+25
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,31 @@ default boolean startsWith(Iterable<? extends T> that, int offset) {
11261126
@Override
11271127
<U> Seq<T> distinctBy(Function<? super T, ? extends U> keyExtractor);
11281128

1129+
/**
1130+
* Returns a sequential collection where duplicate elements, as determined by the comparator,
1131+
* are removed. If duplicates are encountered, the last occurrence of the element is
1132+
* retained in the resulting sequence.
1133+
*
1134+
* @param comparator the comparator used to determine equality of elements for the purpose
1135+
* of identifying duplicates. The comparator defines if two elements are
1136+
* considered equal.
1137+
* @return a new sequence where duplicates are removed, retaining the last occurrence of
1138+
* each element as per the comparator provided.
1139+
*/
1140+
Seq<T> distinctByKeepLast(Comparator<? super T> comparator);
1141+
1142+
/**
1143+
* Returns a sequential Stream-like sequence where the elements are distinct based on a key
1144+
* derived from the provided keyExtractor function. When duplicate elements are found,
1145+
* the last encountered element is kept in the sequence.
1146+
*
1147+
* @param <U> the type of key extracted from elements of this sequence
1148+
* @param keyExtractor the function to extract the key used for determining uniqueness
1149+
* @return a sequence consisting of distinct elements, keeping the last occurrence of each
1150+
* duplicate based on the extracted key
1151+
*/
1152+
<U> Seq<T> distinctByKeepLast(Function<? super T, ? extends U> keyExtractor);
1153+
11291154
@Override
11301155
Seq<T> drop(int n);
11311156

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

+12
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,18 @@ default <U> Stream<T> distinctBy(Function<? super T, ? extends U> keyExtractor)
992992
return filter(t -> seen.add(keyExtractor.apply(t)));
993993
}
994994

995+
@Override
996+
default Stream<T> distinctByKeepLast(Comparator<? super T> comparator) {
997+
Objects.requireNonNull(comparator, "comparator is null");
998+
return ofAll(iterator().distinctByKeepLast(comparator));
999+
}
1000+
1001+
@Override
1002+
default <U> Stream<T> distinctByKeepLast(Function<? super T, ? extends U> keyExtractor) {
1003+
Objects.requireNonNull(keyExtractor, "keyExtractor is null");
1004+
return ofAll(iterator().distinctByKeepLast(keyExtractor));
1005+
}
1006+
9951007
@Override
9961008
default Stream<T> drop(int n) {
9971009
Stream<T> stream = this;

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

+12
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,18 @@ public <U> Vector<T> distinctBy(Function<? super T, ? extends U> keyExtractor) {
685685
return filter(t -> seen.add(keyExtractor.apply(t)));
686686
}
687687

688+
@Override
689+
public Vector<T> distinctByKeepLast(Comparator<? super T> comparator) {
690+
Objects.requireNonNull(comparator, "comparator is null");
691+
return ofAll(iterator().distinctByKeepLast(comparator));
692+
}
693+
694+
@Override
695+
public <U> Vector<T> distinctByKeepLast(Function<? super T, ? extends U> keyExtractor) {
696+
Objects.requireNonNull(keyExtractor, "keyExtractor is null");
697+
return ofAll(iterator().distinctByKeepLast(keyExtractor));
698+
}
699+
688700
@Override
689701
public Vector<T> drop(int n) {
690702
return wrap(trie.drop(n));

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

+54
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@
2424
import java.math.BigDecimal;
2525
import java.util.ArrayList;
2626
import java.util.Arrays;
27+
import java.util.Comparator;
2728
import java.util.Spliterator;
2829
import java.util.function.Function;
2930
import java.util.function.Predicate;
3031
import java.util.stream.Collector;
3132
import org.junit.jupiter.api.Test;
33+
import org.junit.jupiter.api.TestTemplate;
3234

35+
import static java.util.Comparator.comparingInt;
3336
import static org.assertj.core.api.Assertions.assertThatThrownBy;
3437
import static org.junit.jupiter.api.Assertions.assertThrows;
3538

@@ -2286,6 +2289,57 @@ public void shouldTestIndexedSeqEndsWithNonIndexedSeq() {
22862289
assertThat(of(1, 2, 3, 4).endsWith(Stream.of(2, 3, 5))).isFalse();
22872290
}
22882291

2292+
// -- distinctByKeepLast(Comparator)
2293+
2294+
@TestTemplate
2295+
public void shouldComputeDistinctByKeepLastOfEmptySeqUsingComparator() {
2296+
final Comparator<Integer> comparator = comparingInt(i -> i);
2297+
if (useIsEqualToInsteadOfIsSameAs()) {
2298+
assertThat(this.<Integer>empty().distinctByKeepLast(comparator)).isEqualTo(empty());
2299+
} else {
2300+
assertThat(this.<Integer>empty().distinctByKeepLast(comparator)).isSameAs(empty());
2301+
}
2302+
}
2303+
2304+
@TestTemplate
2305+
public void shouldComputeDistinctByKeepLastOfNonEmptySeqUsingComparator() {
2306+
final Comparator<String> comparator = comparingInt(s -> (s.charAt(1)));
2307+
final Seq<String> distinct = of("1a", "2a", "3b", "4b", "3a", "5c")
2308+
.distinctByKeepLast(comparator);
2309+
assertThat(distinct).isEqualTo(of("4b", "3a", "5c"));
2310+
}
2311+
2312+
@TestTemplate
2313+
public void shouldReturnSameInstanceWhenDistinctByKeepLastComparatorEmptySeq() {
2314+
final Seq<?> empty = empty();
2315+
assertThat(empty.distinctByKeepLast(Comparators.naturalComparator())).isSameAs(empty);
2316+
}
2317+
2318+
// -- distinctByKeepLast(Function)
2319+
2320+
@TestTemplate
2321+
public void shouldComputeDistinctByKeepLastOfEmptySeqUsingKeyExtractor() {
2322+
if (useIsEqualToInsteadOfIsSameAs()) {
2323+
assertThat(empty().distinctByKeepLast(Function.identity())).isEqualTo(empty());
2324+
} else {
2325+
assertThat(empty().distinctByKeepLast(Function.identity())).isSameAs(empty());
2326+
}
2327+
}
2328+
2329+
@TestTemplate
2330+
public void shouldComputeDistinctByKeepLastOfNonEmptySeqUsingKeyExtractor() {
2331+
final Function<String, Character> function = c -> c.charAt(1);
2332+
final Seq<String> distinct = of("1a", "2a", "3b", "4b", "3a", "5c")
2333+
.distinctByKeepLast(function);
2334+
assertThat(distinct).isEqualTo(of("4b", "3a", "5c"));
2335+
}
2336+
2337+
@TestTemplate
2338+
public void shouldReturnSameInstanceWhenDistinctByKeepLastFunctionEmptySeq() {
2339+
final Seq<?> empty = empty();
2340+
assertThat(empty.distinctByKeepLast(Function.identity())).isSameAs(empty);
2341+
}
2342+
22892343
private interface SomeInterface {
22902344
}
22912345

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

+28
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,34 @@ public void shouldComputeDistinctByOfNonEmptyTraversableUsingKeyExtractor() {
664664
.isEqualTo(CharSeq.of('1', '2', '3', '4', '5'));
665665
}
666666

667+
// -- distinctByKeepLast(Comparator)
668+
669+
@Test
670+
public void shouldComputeDistinctByKeepLastOfEmptyTraversableUsingComparator() {
671+
final Comparator<Character> comparator = Comparator.comparingInt(i -> i);
672+
assertThat(CharSeq.empty().distinctByKeepLast(comparator)).isSameAs(CharSeq.empty());
673+
}
674+
675+
@Test
676+
public void shouldComputeDistinctByKeepLastOfNonEmptyTraversableUsingComparator() {
677+
final Comparator<Character> comparator = (s1, s2) -> (s1 - s2);
678+
assertThat(CharSeq.of('1', '2', '3', '3', '4', '5').distinctByKeepLast(comparator))
679+
.isEqualTo(CharSeq.of('1', '2', '3', '4', '5'));
680+
}
681+
682+
// -- distinctByKeepLast(Function)
683+
684+
@Test
685+
public void shouldComputeDistinctByKeepLastOfEmptyTraversableUsingKeyExtractor() {
686+
assertThat(CharSeq.empty().distinctByKeepLast(Function.identity())).isSameAs(CharSeq.empty());
687+
}
688+
689+
@Test
690+
public void shouldComputeDistinctByKeepLastOfNonEmptyTraversableUsingKeyExtractor() {
691+
assertThat(CharSeq.of('1', '2', '3', '2', '4', '5').distinctByKeepLast(c -> c))
692+
.isEqualTo(CharSeq.of('1', '3', '2', '4', '5'));
693+
}
694+
667695
// -- drop
668696

669697
@Test

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

+2
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,8 @@ public void multipleHasNext() {
604604
multipleHasNext(() -> Iterator.of(1, 2, 1, 2, 1, 2).distinct());
605605
multipleHasNext(() -> Iterator.of(1, 2, 1, 2, 1, 2).distinctBy(e -> e % 2));
606606
multipleHasNext(() -> Iterator.of(1, 2, 1, 2, 1, 2).distinctBy(Comparator.comparingInt(e -> e % 2)));
607+
multipleHasNext(() -> Iterator.of(1, 2, 1, 2, 1, 2).distinctByKeepLast(e -> e % 2));
608+
multipleHasNext(() -> Iterator.of(1, 2, 1, 2, 1, 2).distinctByKeepLast(Comparator.comparingInt(e -> e % 2)));
607609
multipleHasNext(() -> Iterator.of(1, 2, 3, 4).drop(1));
608610
multipleHasNext(() -> Iterator.of(1, 2, 3, 4).dropRight(1));
609611
multipleHasNext(() -> Iterator.of(1, 2, 3, 4).dropUntil(e -> e == 3));

0 commit comments

Comments
 (0)