Remove first(with zero index) element from stream conditionally - java

I have following code:
Stream<String> lines = reader.lines();
If fist string equals "email" I want to remove first string from the Stream. For other strings from the stream I don't need this check.
How could I acheve it?
P.S.
Sure I can transform it to the list, then use old school for loop but further I need stream again.

While the reader will be in an unspecified state after you constructed a stream of lines from it, it is in a well defined state before you do it.
So you can do
String firstLine = reader.readLine();
Stream<String> lines = reader.lines();
if(firstLine != null && !"email".equals(firstLine))
lines = Stream.concat(Stream.of(firstLine), lines);
Which is the cleanest solution in my opinion. Note that this is not the same as Java 9’s dropWhile, which would drop more than one line if they match.

If you cannot have the list and must do it with only a Stream, you can do it with a variable.
The thing is that you can only use a variable if it is "final" or "effectively final" so you cannot use a literal boolean. You can still do it with an AtomicBoolean :
Stream<String> stream = Arrays.asList("test", "email", "foo").stream();
AtomicBoolean first = new AtomicBoolean(true);
stream.filter(s -> {
if (first.compareAndSet(true, false)) {
return !s.equals("email");
}
return true;
})
// Then here, do whatever you need
.forEach(System.out::println);
Note : I don't like using "external variables" in a Stream because side effects are a bad practice in the functional programming paradigm. Better options are welcome.

To avoid checking the condition on each line of the file, I'd simply read and check the first line separately, then run the pipeline on rest of lines without checking the condition:
String first = reader.readLine();
Stream<String> firstLines = Optional.of(first)
.filter(s -> !"email".equals(s))
.map(s -> Stream.of(s))
.orElseGet(() -> Stream.empty());
Stream<String> lines = Stream.concat(firstLines, reader.lines());
Simpler on Java 9+:
Stream<String> firstLines = Optional.of(first)
.filter(s -> !"email".equals(s))
.stream();
Stream<String> lines = Stream.concat(firstLines, reader.lines());

To filter elements based on their index, you can use AtomicInteger to store and increment index while processing a Stream:
private static void filter(Stream<String> stream) {
AtomicInteger index = new AtomicInteger();
List<String> result = stream
.filter(el -> {
int i = index.getAndIncrement();
return i > 0 || (i == 0 && !"email".equals(el));
})
.collect(toList());
System.out.println(result);
}
public static void main(String[] args) {
filter(Stream.of("email", "test1", "test2", "test3"));
//[test1, test2, test3]
filter(Stream.of("test1", "email", "test2", "test3"));
//[test1, email, test2, test3]
filter(Stream.of("test1", "test2", "test3"));
//[test1, test2, test3]
}
This approach allows to filter elements at any index, not only the first one.

A little more convoluted, getting some inspiration from this snippet.
You can create a Stream<Integer> that will represent indexes and "zip" it with your Stream<String> to create a Stream<Pair<String, Integer>>
Then filter using the index and map it back to a Stream<String>
public static void main(String[] args) {
Stream<String> s = reader.lines();
Stream<Integer> indexes = Stream.iterate(0, i -> i + 1);
zip(s, indexes)
.filter(pair -> !(pair.getKey().equals("email") && pair.getValue() == 0))
.map(Pair::getKey)
.forEach(System.out::println);
}
private static Stream<Pair<String,Integer>> zip(Stream<String> stringStream, Stream<Integer> indexesStream){
Iterable<Pair<String,Integer>> iterable = () -> new ZippedWithIndexIterator(stringStream.iterator(), indexesStream.iterator());
return StreamSupport.stream(iterable.spliterator(), false);
}
static class ZippedWithIndexIterator implements Iterator<Pair<String, Integer>> {
private final Iterator<String> stringIterator;
private final Iterator<Integer> integerIterator;
ZippedWithIndexIterator(Iterator<String> stringIterator, Iterator<Integer> integerIterator) {
this.stringIterator = stringIterator;
this.integerIterator = integerIterator;
}
#Override
public Pair<String, Integer> next() {
return new Pair<>(stringIterator.next(), integerIterator.next());
}
#Override
public boolean hasNext() {
return stringIterator.hasNext() && integerIterator.hasNext();
}
}

#Arnouds answer is correct. You can create one stream for first line and then compare as below,
Stream<String> firstLineStream = reader.lines().limit(1).filter(line -> !line.startsWith("email"));;
Stream<String> remainingLinesStream = reader.lines().skip(1);
Stream.concat(firstLineStream, remainingLinesStream);

Here is example with Collectors.reducing. But in the end creates a list anyway.
Stream<String> lines = Arrays.asList("email", "aaa", "bbb", "ccc")
.stream();
List reduceList = (List) lines
.collect(Collectors.reducing( new ArrayList<String>(), (a, v) -> {
List list = (List) a;
if (!(list.isEmpty() && v.equals("email"))) {
list.add(v);
}
return a;
}));
reduceList.forEach(System.out::println);

Try this:
MutableBoolean isFirst = MutableBoolean.of(true);
lines..dropWhile(e -> isFirst.getAndSet(false) && "email".equals(e))

Related

Can I add element while using Java stream groupingby

For loop code is this.
Param : ArrayList userList
Map<String, User> map = new HashMap();
for (User user : userList) {
String[] arr = user.getStringSeq().split(DELIMITER);
String key = String.join(DELIMITER, arr[MENU_IDX], arr[GROUP_IDX]);
if (Objects.isNull(map.get(key))) {
Set<IOType> ioTypeSet = new HashSet<>();
ioTypeSet.add(IOType.valueOf(arr[IO_TYPE_IDX]));
user.setIoTypes(ioTypeSet);
map.put(key, user);
} else {
map.get(key).getIoTypes().add(IOType.valueOf(arr[IO_TYPE_IDX]));
}
}
and i want to modify stream
List<List<user>> userList = userList
.stream()
.collect(groupingBy(
e -> {
String[] arr = e.getStringSeq().split(DELIMITER);
return String.join(DELIMITER, arr[0], arr[1]);
},
mapping(e -> {
IOType ioType = IOType.valueOf(e.getNavAuthSeq().split(DELIMITER)[2]);
User user = new User();
user.addIoType(ioType);
return user;
}, toList())
)).values()
.stream()
.toList();
my stream code grouping list succefully
but i want to remove same key element and put splited string
like this
List<List<user>> userList = userList
.stream()
.collect(groupingBy(
e -> {
String[] arr = e.getStringSeq().split(DELIMITER);
return String.join(DELIMITER, arr[0], arr[1]);
},
mapping(e -> {
if (e.getIoTypes() != null) {
e.getIoTypes().add(IOType.NONE);
return null;
} else {
IOType ioType = IOType.valueOf(e.getStringSeq().split(DELIMITER)[2]);
UserNavAuthsLoginDTO userNavAuthsLoginDTO = new UserNavAuthsLoginDTO();
userNavAuthsLoginDTO.addIoType(ioType);
return userNavAuthsLoginDTO;
}
}, toList())
)).values()
.stream()
.toList();
but if else code doesn't work
can i resove this problem?
If you want to discard certain elements after inside the Collector after groupingBy, you can wrap mapping() with Collector filtering(). It expects a Predicate and retains only elements for which predicate gets evaluated to true.
.collect(Collectors.groupingBy(
e -> { }, // classifier Function of groupingBy
Collectors.filtering(e -> { }, // Predicate of filtering
Collectors.mapping(e -> { }, // mapper Function of mapping
Collectors.toList())
)
))
Note that there's a difference between using filter() operation and Collector filtering(). Imagine a scenario when all elements mapped to a particular Key don't pass the predicate. In this case, the entry with this Key would be present in the resulting Map (and its Value would be an empty list). And if you apply filter() in the stream - there wouldn't be such entry.
Alternatively, if it's not important to filter out elements after the grouping phase, you can use filter() operation, that would be a preferred way in such case.
Also, worth to point out that you're performing side-effects on the mutable function parameter inside mapping() (to put it simple, anything that a function doesn't besides computing its resulting value is a side-effect). I'm not claiming that it will break things somehow, but it's definitely not very clean.

Convert for-loop and if condition to stream, filter, collect

I am trying to convert this for loop and if condition to Java streams, filter and collect
final List<Optional<String>> students = listofstudents.get();
final List<String> finalist = new ArrayList<>(students.size());
for(int i=0; i<students.size(); i++) {
if(students.get(i).isPresent()) {
finalist.add(students.get(i).get());
}
else {
finalist.add(null);
}
}
I tried this:
List<String>result = null;
students.stream()
.filter(e -> e.isPresent())
.map(e->result.add(e))
.orElse(result.add(null)
.collect(Collectors.toList());
However, this is completely wrong. I could not make it work as expected
can someone tell me how should i do it using stream, filter, collect?
You could use the ternary conditional operator when mapping:
List<String> result = students.stream()
.map(op -> op.isPresent()? op.get() : null)
.collect(Collectors.toList());
Or as #Mark Rotteveel correctly commented:
students.stream().map(op -> op.orElse(null)).collect(Collectors.toList());

How to filter based on list returned by map param using Java 8 streams

I'm trying to use Java stream to filter some values based on certain conditions. I am able to achieve the same using traditional for loops and a little bit of streams, but I want to rewrite the same logic fully in streams.
Original code:
public List <String> getProductNames(Hub hub, String requestedGroup) {
List <SupportedProduct> configuredProducts = repo.getSupportedProducts(hub);
List <String> productNames = new ArrayList <> ();
for (SupportedProduct supportedProduct: configuredProducts) {
List < String > categoryNameList = new ArrayList <> ();
String activeCategoryName = supportedProduct.getCategoryDetails().getActiveCategoryName();
if (activeCategoryName == null) {
Optional.ofNullable(supportedProduct.getCategoryDetails().getCategories())
.orElse(Collections.emptyList())
.forEach(category - > categoryNameList.add(category.getName()));
} else {
categoryNameList.add(activeCategoryName);
}
for (String catName: categoryNameList) {
Division division = divisionRepo.getDivisionByCatName(catName);
if (division != null && division.getGroup() == requestedGroup) {
productNames.add(supportedProduct.getProductName());
}
}
}
return productNames;
}
My try:
return Optional.ofNullable(configuredProducts).orElse(Collections.emptyList()).stream()
.map(supportedProduct -> {
List<String> categoryNameList = new ArrayList<>();
String activeCategoryName = supportedProduct.getCategoryDetails().getActiveCategoryName();
if (activeCategoryName == null) {
Optional.ofNullable(supportedProduct.getCategoryDetails().getCategories())
.orElse(Collections.emptyList())
.forEach(category -> categoryNameList.add(category.getName()));
} else {
categoryNameList.add(activeCategoryName);
}
return categoryNameList;
})
.filter(catName ->{
Division division = divisionRepo.getDivisionByCatName(catName);
return division != null && division.getGroup() == requestedGroup;
})........
But I'm lost beyond this.
Please help me to write the same using streams.
EDIT: Added IDEOne for testing - Link
The logic inside is quite complicated, however, try this out:
public List <String> getProductNames(Hub hub, String requestedGroup) {
List<SupportedProduct> configuredProducts = repo.getSupportedProducts(hub);
// extract pairs:
// key=SupportedProduct::getProductName
// values=List with one activeCategoryName OR names of all the categories
Map<String, List<String>> namedActiveCategoryNamesMap = configuredProducts.stream()
.collect(Collectors.toMap(
SupportedProduct::getProductName,
p -> Optional.ofNullable(p.getCategoryDetails().getActiveCategoryName())
.map(Collections::singletonList)
.orElse(Optional.ofNullable(p.getCategoryDetails().getCategories())
.stream()
.flatMap(Collection::stream)
.map(Category::getName)
.collect(Collectors.toList()))));
// look-up based on the categories' names, group equality comparison and returning a List
return namedActiveCategoryNamesMap.entrySet().stream()
.filter(entry -> entry.getValue().stream()
.map(catName -> divisionRepo.getDivisionByCatName(catName))
.filter(Objects::nonNull)
.map(Division::getGroup)
.anyMatch(requestedGroup::equals))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
I recommend splitting into separate methods for sake of readability (the best way to go).
The verbose logics of Optional chains including two orElse calls can be surely simplified, however, it gives you the idea.
You can perform within one Stream using Collectors.collectingAndThen. In that case, I'd extract the Function finisher elsewhere, example:
public List<String> getProductNames(Hub hub, String requestedGroup) {
return repo.getSupportedProducts(hub).stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(
SupportedProduct::getProductName,
categoryNamesFunction()),
productNamesFunction(requestedGroup)));
}
private Function<Map<String, List<String>>, List<String>> productNamesFunction(String requestedGroup) {
return map -> map.entrySet().stream()
.filter(entry -> entry.getValue().stream()
.map(divisionRepo::getDivisionByCatName)
.filter(Objects::nonNull)
.map(Division::getGroup)
.anyMatch(requestedGroup::equals))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
private Function<SupportedProduct, List<String>> categoryNamesFunction() {
return p -> Optional.ofNullable(p.getCategoryDetails().getActiveCategoryName())
.map(Collections::singletonList)
.orElse(Optional.ofNullable(p.getCategoryDetails().getCategories())
.stream()
.flatMap(Collection::stream)
.map(Category::getName)
.collect(Collectors.toList()));
}

Learning java 8

I have an existing code in Java that I would like to write in Java 8.
Could someone provide suggestions of porting this code to Java 8.
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> result = new ArrayList();
Map<String, List<String>> map = new HashMap();
if (strs == null || strs.length == 0) return result;
for (String word: strs) {
char[] wordArr = word.toCharArray();
Arrays.sort(wordArr);
String sortedWord = new String(wordArr);
if (map.containsKey(sortedWord)) {
List<String> list = map.get(sortedWord);
list.add(word);
map.put(sortedWord, list);
} else {
List<String> list = new ArrayList();
list.add(word);
map.put(sortedWord, list);
}
}
return new ArrayList(map.values());
}
}
UPDATE - I have used getOrDefault()
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> result = new ArrayList();
Map<String, List<String>> map = new HashMap();
if (strs == null || strs.length == 0) return result;
for (String word: strs) {
char[] wordArr = word.toCharArray();
Arrays.sort(wordArr);
String sortedWord = new String(wordArr);
List<String> list = map.getOrDefault(sortedWord, new ArrayList());
list.add(word);
map.put(sortedWord, list);
}
return new ArrayList(map.values());
}
}
Any other way ?
Here’s the best solution I could come up with using streams:
public class Solution {
private static String sortString(String str) {
char[] chars = str.toCharArray();
Arrays.sort(chars);
return new String(chars);
}
public List<List<String>> groupAnagrams(String[] strs) {
if (strs == null) {
return new ArrayList<>();
}
return new ArrayList<>(Arrays.stream(strs)
.collect(Collectors.groupingBy(Solution::sortString))
.values());
}
}
Notes:
Starting simple, we create a stream from the array strs using the utility method Arrays.stream(array):
Arrays.stream(strs);
The Stream#collect(collector) method takes a stream and converts it into a standard collection type (Map, List, etc.):
Arrays.stream(strs)
.collect(...);
In this instance, we’re going to use Collectors.groupingBy(classifier). This collector sorts your stream into ‘bins’, with the bin label specified by the result of passing each value through the classifier lambda function. The return type is a Map<K, List<V>> (where K is the label type and V is whatever type we’re streaming) - the same shape you used for your map variable:
Arrays.stream(strs)
.collect(Collectors.groupingBy(...));
But what function do we pass .groupingBy()? Since you’re looking to group by the sorted string, we need a function which will create a sorted string from a regular one. We’re going to use your existing function for this:
Arrays.stream(strs)
.collect(Collectors.groupingBy(str -> {
char[] chars = word.toCharArray();
Arrays.sort(chars);
return new String(chars);
}));
In the interest of being streamy though, we replace that function with another stream*: (It turns out chars don’t stream nicely)
In the interest of neatness, I’ve refactored the lambda function into a static method on your class:
private static String sortString(String str) {
char[] chars = str.toCharArray();
Arrays.sort(chars);
return new String(chars);
}
Arrays.stream(strs)
.collect(Collectors.groupingBy(str -> {
return Solution.sortString(str);
}));
Since we’re making a single call to a function which takes only one parameter, we can use a method reference instead of the lambda function:
Arrays.stream(strs)
.collect(Collectors.groupingBy(Solution::sortString));
The rest is very similar to your existing code. Using Map#values() to strip the keys from the dataset, then wrapping the resulting Collection in an ArrayList:
new ArrayList<>(Arrays.stream(strs)
.collect(Collectors.groupingBy(Solution::sortString))
.values());
You're essentially looking for the groupingBy collector:
List<List<String>> groupAnagrams(String[] strs) {
if (strs == null || strs.length == 0)
return new ArrayList<>();
return new ArrayList<>(Arrays.stream(strs)
.collect(groupingBy(s -> Arrays.stream(s.split(""))
.sorted()
.collect(joining())))
.values());
}
First, we create a Stream upon invoking Arrays.stream(strs) which then enables us to perform a reduction operation using collect, this method consumes a Collector where this Collector is a groupingBy.
This overload of the groupingBy collector produces a Map<K, List<T>> where keys are the values resulting from applying the classification function to the input elements (i.e. Arrays.stream(s.split("")).sorted().collect(joining())), and whose corresponding values are Lists containing the input elements which map to the associated key under the classification function.
Finally, we call values() to retrieve a Collection<List<String>> and pass this to the ArrayList constructor and return.
Reading you might find interesting:
Collectors documentation
Streams documentation
Introduction to Java 8 Streams
Java 8: An Introduction to Streams
Answer from Aomine is good, but it is possible to rewrite shorter:
return strs == null? Collection.emptyList() : new ArrayList<>(Arrays.stream(strs)
.collect(groupingBy(s -> Arrays.stream(s.split(""))
.sorted()
.collect(joining())))
.values());
This answer provides a java-stream solution which personally I would proceed with. As for your current implementation, you're quite close but computeIfAbsent would be the better approach to proceed with:
List<List<String>> groupAnagrams(String[] strs) {
if (strs == null || strs.length == 0) return new ArrayList<>();
Map<String, List<String>> map = new HashMap<>();
for (String word : strs) {
char[] wordArr = word.toCharArray();
Arrays.sort(wordArr);
map.computeIfAbsent(new String(wordArr), k -> new ArrayList<>()).add(word);
}
return new ArrayList<>(map.values());
}
I am pretty sure you know what most of the code does so I'll only explain the computeIfAbsent part above by quoting most of the documentation as it's the best explanation.
if the specified key (new String(wordArr)) is not already associated
with a value (or is mapped to null), attempts to compute its value
using the given mapping function (k -> new ArrayList<>()) and enters
it into this map unless null.
next time we attempt to add a key that is already present in the map, the existing list associated with the specified key is returned and we add the word into it with .add(word) above.
If you want to use Streams refer to the other answers, if you want to know how yo use Map operation to simplify your if/else here it is :
It the key is not in the map, add it with an empty list, then get that list and add the word :
for (String word : strs) {
char[] wordArr = word.toCharArray();
Arrays.sort(wordArr);
String sortedWord = new String(wordArr);
map.putIfAbsent(sortedWord, new ArrayList<>());
map.get(sortedWord).add(word);
}
Using Optional and streams and some static imports:
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.Collections.emptyList;
import static java.util.Optional.of;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.joining;
import static org.apache.commons.lang3.StringUtils.EMPTY;
public class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
Collection<List<String>> collection = of(asList(strs))
.orElse(emptyList())
.stream()
.collect(groupingBy(word -> stream(word.split(EMPTY))
.sorted()
.collect(joining())))
.values();
return new ArrayList<>(collection);
}
}

Is there a concise way to iterate over a stream with indices in Java 8?

Is there a concise way to iterate over a stream whilst having access to the index in the stream?
String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList;
Stream<Integer> indices = intRange(1, names.length).boxed();
nameList = zip(indices, stream(names), SimpleEntry::new)
.filter(e -> e.getValue().length() <= e.getKey())
.map(Entry::getValue)
.collect(toList());
which seems rather disappointing compared to the LINQ example given there
string[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
var nameList = names.Where((c, index) => c.Length <= index + 1).ToList();
Is there a more concise way?
Further it seems the zip has either moved or been removed...
The cleanest way is to start from a stream of indices:
String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
IntStream.range(0, names.length)
.filter(i -> names[i].length() <= i)
.mapToObj(i -> names[i])
.collect(Collectors.toList());
The resulting list contains "Erik" only.
One alternative which looks more familiar when you are used to for loops would be to maintain an ad hoc counter using a mutable object, for example an AtomicInteger:
String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
AtomicInteger index = new AtomicInteger();
List<String> list = Arrays.stream(names)
.filter(n -> n.length() <= index.incrementAndGet())
.collect(Collectors.toList());
Note that using the latter method on a parallel stream could break as the items would not necesarily be processed "in order".
The Java 8 streams API lacks the features of getting the index of a stream element as well as the ability to zip streams together. This is unfortunate, as it makes certain applications (like the LINQ challenges) more difficult than they would be otherwise.
There are often workarounds, however. Usually this can be done by "driving" the stream with an integer range, and taking advantage of the fact that the original elements are often in an array or in a collection accessible by index. For example, the Challenge 2 problem can be solved this way:
String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList =
IntStream.range(0, names.length)
.filter(i -> names[i].length() <= i)
.mapToObj(i -> names[i])
.collect(toList());
As I mentioned above, this takes advantage of the fact that the data source (the names array) is directly indexable. If it weren't, this technique wouldn't work.
I'll admit that this doesn't satisfy the intent of Challenge 2. Nonetheless it does solve the problem reasonably effectively.
EDIT
My previous code example used flatMap to fuse the filter and map operations, but this was cumbersome and provided no advantage. I've updated the example per the comment from Holger.
Since guava 21, you can use
Streams.mapWithIndex()
Example (from official doc):
Streams.mapWithIndex(
Stream.of("a", "b", "c"),
(str, index) -> str + ":" + index)
) // will return Stream.of("a:0", "b:1", "c:2")
I've used the following solution in my project. I think it is better than using mutable objects or integer ranges.
import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;
import java.util.stream.Collector.Characteristics;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static java.util.Objects.requireNonNull;
public class CollectionUtils {
private CollectionUtils() { }
/**
* Converts an {#link java.util.Iterator} to {#link java.util.stream.Stream}.
*/
public static <T> Stream<T> iterate(Iterator<? extends T> iterator) {
int characteristics = Spliterator.ORDERED | Spliterator.IMMUTABLE;
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, characteristics), false);
}
/**
* Zips the specified stream with its indices.
*/
public static <T> Stream<Map.Entry<Integer, T>> zipWithIndex(Stream<? extends T> stream) {
return iterate(new Iterator<Map.Entry<Integer, T>>() {
private final Iterator<? extends T> streamIterator = stream.iterator();
private int index = 0;
#Override
public boolean hasNext() {
return streamIterator.hasNext();
}
#Override
public Map.Entry<Integer, T> next() {
return new AbstractMap.SimpleImmutableEntry<>(index++, streamIterator.next());
}
});
}
/**
* Returns a stream consisting of the results of applying the given two-arguments function to the elements of this stream.
* The first argument of the function is the element index and the second one - the element value.
*/
public static <T, R> Stream<R> mapWithIndex(Stream<? extends T> stream, BiFunction<Integer, ? super T, ? extends R> mapper) {
return zipWithIndex(stream).map(entry -> mapper.apply(entry.getKey(), entry.getValue()));
}
public static void main(String[] args) {
String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
System.out.println("Test zipWithIndex");
zipWithIndex(Arrays.stream(names)).forEach(entry -> System.out.println(entry));
System.out.println();
System.out.println("Test mapWithIndex");
mapWithIndex(Arrays.stream(names), (Integer index, String name) -> index+"="+name).forEach((String s) -> System.out.println(s));
}
}
In addition to protonpack, jOOλ's Seq provides this functionality (and by extension libraries that build on it like cyclops-react, I am the author of this library).
Seq.seq(Stream.of(names)).zipWithIndex()
.filter( namesWithIndex -> namesWithIndex.v1.length() <= namesWithIndex.v2 + 1)
.toList();
Seq also supports just Seq.of(names) and will build a JDK Stream under the covers.
The simple-react equivalent would similarly look like
LazyFutureStream.of(names)
.zipWithIndex()
.filter( namesWithIndex -> namesWithIndex.v1.length() <= namesWithIndex.v2 + 1)
.toList();
The simple-react version is more tailored for asynchronous / concurrent processing.
Just for completeness here's the solution involving my StreamEx library:
String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
EntryStream.of(names)
.filterKeyValue((idx, str) -> str.length() <= idx+1)
.values().toList();
Here we create an EntryStream<Integer, String> which extends Stream<Entry<Integer, String>> and adds some specific operations like filterKeyValue or values. Also toList() shortcut is used.
I found the solutions here when the Stream is created of list or array (and you know the size). But what if Stream is with unknown size? In this case try this variant:
public class WithIndex<T> {
private int index;
private T value;
WithIndex(int index, T value) {
this.index = index;
this.value = value;
}
public int index() {
return index;
}
public T value() {
return value;
}
#Override
public String toString() {
return value + "(" + index + ")";
}
public static <T> Function<T, WithIndex<T>> indexed() {
return new Function<T, WithIndex<T>>() {
int index = 0;
#Override
public WithIndex<T> apply(T t) {
return new WithIndex<>(index++, t);
}
};
}
}
Usage:
public static void main(String[] args) {
Stream<String> stream = Stream.of("a", "b", "c", "d", "e");
stream.map(WithIndex.indexed()).forEachOrdered(e -> {
System.out.println(e.index() + " -> " + e.value());
});
}
With a List you can try
List<String> strings = new ArrayList<>(Arrays.asList("First", "Second", "Third", "Fourth", "Fifth")); // An example list of Strings
strings.stream() // Turn the list into a Stream
.collect(HashMap::new, (h, o) -> h.put(h.size(), o), (h, o) -> {}) // Create a map of the index to the object
.forEach((i, o) -> { // Now we can use a BiConsumer forEach!
System.out.println(String.format("%d => %s", i, o));
});
Output:
0 => First
1 => Second
2 => Third
3 => Fourth
4 => Fifth
If you happen to use Vavr(formerly known as Javaslang), you can leverage the dedicated method:
Stream.of("A", "B", "C")
.zipWithIndex();
If we print out the content, we will see something interesting:
Stream((A, 0), ?)
This is because Streams are lazy and we have no clue about next items in the stream.
Here is code by abacus-common
Stream.of(names).indexed()
.filter(e -> e.value().length() <= e.index())
.map(Indexed::value).toList();
Disclosure: I'm the developer of abacus-common.
There isn't a way to iterate over a Stream whilst having access to the index because a Stream is unlike any Collection. A Stream is merely a pipeline for carrying data from one place to another, as stated in the documentation:
No storage. A stream is not a data structure that stores elements; instead, they carry values from a source (which could be a data structure, a generator, an IO channel, etc) through a pipeline of computational operations.
Of course, as you appear to be hinting at in your question, you could always convert your Stream<V> to a Collection<V>, such as a List<V>, in which you will have access to the indexes.
With https://github.com/poetix/protonpack
u can do that zip:
String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList;
Stream<Integer> indices = IntStream.range(0, names.length).boxed();
nameList = StreamUtils.zip(indices, stream(names),SimpleEntry::new)
.filter(e -> e.getValue().length() <= e.getKey()).map(Entry::getValue).collect(toList());
System.out.println(nameList);
If you don't mind using a third-party library, Eclipse Collections has zipWithIndex and forEachWithIndex available for use across many types. Here's a set of solutions to this challenge for both JDK types and Eclipse Collections types using zipWithIndex.
String[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
ImmutableList<String> expected = Lists.immutable.with("Erik");
Predicate<Pair<String, Integer>> predicate =
pair -> pair.getOne().length() <= pair.getTwo() + 1;
// JDK Types
List<String> strings1 = ArrayIterate.zipWithIndex(names)
.collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings1);
List<String> list = Arrays.asList(names);
List<String> strings2 = ListAdapter.adapt(list)
.zipWithIndex()
.collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings2);
// Eclipse Collections types
MutableList<String> mutableNames = Lists.mutable.with(names);
MutableList<String> strings3 = mutableNames.zipWithIndex()
.collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings3);
ImmutableList<String> immutableNames = Lists.immutable.with(names);
ImmutableList<String> strings4 = immutableNames.zipWithIndex()
.collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings4);
MutableList<String> strings5 = mutableNames.asLazy()
.zipWithIndex()
.collectIf(predicate, Pair::getOne, Lists.mutable.empty());
Assert.assertEquals(expected, strings5);
Here's a solution using forEachWithIndex instead.
MutableList<String> mutableNames =
Lists.mutable.with("Sam", "Pamela", "Dave", "Pascal", "Erik");
ImmutableList<String> expected = Lists.immutable.with("Erik");
List<String> actual = Lists.mutable.empty();
mutableNames.forEachWithIndex((name, index) -> {
if (name.length() <= index + 1)
actual.add(name);
});
Assert.assertEquals(expected, actual);
If you change the lambdas to anonymous inner classes above, then all of these code examples will work in Java 5 - 7 as well.
Note: I am a committer for Eclipse Collections
You can use IntStream.iterate() to get the index:
String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList = IntStream.iterate(0, i -> i < names.length, i -> i + 1)
.filter(i -> names[i].length() <= i)
.mapToObj(i -> names[i])
.collect(Collectors.toList());
This only works for Java 9 upwards in Java 8 you can use this:
String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList = IntStream.iterate(0, i -> i + 1)
.limit(names.length)
.filter(i -> names[i].length() <= i)
.mapToObj(i -> names[i])
.collect(Collectors.toList());
If you are trying to get an index based on a predicate, try this:
If you only care about the first index:
OptionalInt index = IntStream.range(0, list.size())
.filter(i -> list.get(i) == 3)
.findFirst();
Or if you want to find multiple indexes:
IntStream.range(0, list.size())
.filter(i -> list.get(i) == 3)
.collect(Collectors.toList());
Add .orElse(-1); in case you want to return a value if it doesn't find it.
One possible way is to index each element on the flow:
AtomicInteger index = new AtomicInteger();
Stream.of(names)
.map(e->new Object() { String n=e; public i=index.getAndIncrement(); })
.filter(o->o.n.length()<=o.i) // or do whatever you want with pairs...
.forEach(o->System.out.println("idx:"+o.i+" nam:"+o.n));
Using an anonymous class along a stream is not well-used while being very useful.
If you need the index in the forEach then this provides a way.
public class IndexedValue {
private final int index;
private final Object value;
public IndexedValue(final int index, final Object value) {
this.index = index;
this.value = value;
}
public int getIndex() {
return index;
}
public Object getValue() {
return value;
}
}
Then use it as follows.
#Test
public void withIndex() {
final List<String> list = Arrays.asList("a", "b");
IntStream.range(0, list.size())
.mapToObj(index -> new IndexedValue(index, list.get(index)))
.forEach(indexValue -> {
System.out.println(String.format("%d, %s",
indexValue.getIndex(),
indexValue.getValue().toString()));
});
}
you don't need a map necessarily
that is the closest lambda to the LINQ example:
int[] idx = new int[] { 0 };
Stream.of(names)
.filter(name -> name.length() <= idx[0]++)
.collect(Collectors.toList());
You can create a static inner class to encapsulate the indexer as I needed to do in example below:
static class Indexer {
int i = 0;
}
public static String getRegex() {
EnumSet<MeasureUnit> range = EnumSet.allOf(MeasureUnit.class);
StringBuilder sb = new StringBuilder();
Indexer indexer = new Indexer();
range.stream().forEach(
measureUnit -> {
sb.append(measureUnit.acronym);
if (indexer.i < range.size() - 1)
sb.append("|");
indexer.i++;
}
);
return sb.toString();
}
This question (Stream Way to get index of first element matching boolean) has marked the current question as a duplicate, so I can not answer it there; I am answering it here.
Here is a generic solution to get the matching index that does not require an external library.
If you have a list.
public static <T> int indexOf(List<T> items, Predicate<T> matches) {
return IntStream.range(0, items.size())
.filter(index -> matches.test(items.get(index)))
.findFirst().orElse(-1);
}
And call it like this:
int index = indexOf(myList, item->item.getId()==100);
And if using a collection, try this one.
public static <T> int indexOf(Collection<T> items, Predicate<T> matches) {
int index = -1;
Iterator<T> it = items.iterator();
while (it.hasNext()) {
index++;
if (matches.test(it.next())) {
return index;
}
}
return -1;
}
String[] namesArray = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
String completeString
= IntStream.range(0,namesArray.length)
.mapToObj(i -> namesArray[i]) // Converting each array element into Object
.map(String::valueOf) // Converting object to String again
.collect(Collectors.joining(",")); // getting a Concat String of all values
System.out.println(completeString);
OUTPUT : Sam,Pamela,Dave,Pascal,Erik
String[] namesArray = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
IntStream.range(0,namesArray.length)
.mapToObj(i -> namesArray[i]) // Converting each array element into Object
.map(String::valueOf) // Converting object to String again
.forEach(s -> {
//You can do various operation on each element here
System.out.println(s);
}); // getting a Concat String of all
To Collect in the List:
String[] namesArray = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> namesList
= IntStream.range(0,namesArray.length)
.mapToObj(i -> namesArray[i]) // Converting each array element into Object
.map(String::valueOf) // Converting object to String again
.collect(Collectors.toList()); // collecting elements in List
System.out.println(listWithIndex);
As jean-baptiste-yunès said, if your stream is based on a java List then using an AtomicInteger and its incrementAndGet method is a very good solution to the problem and the returned integer does correspond to the index in the original List as long as you do not use a parallel stream.
Here's solution for standard Java:
In-line solution:
Arrays.stream("zero,one,two,three,four".split(","))
.map(new Function<String, Map.Entry<Integer, String>>() {
int index;
#Override
public Map.Entry<Integer, String> apply(String s) {
return Map.entry(index++, s);
}
})
.forEach(System.out::println);
and more readable solution with utility method:
static <T> Function<T, Map.Entry<Integer, T>> mapWithIntIndex() {
return new Function<T, Map.Entry<Integer, T>>() {
int index;
#Override
public Map.Entry<Integer, T> apply(T t) {
return Map.entry(index++, t);
}
};
}
...
Arrays.stream("zero,one,two,three,four".split(","))
.map(mapWithIntIndex())
.forEach(System.out::println);
If the list is unique, we can make use of indexOf method.
List<String> names = Arrays.asList("Sam", "Pamela", "Dave", "Pascal", "Erik");
names.forEach(name ->{
System.out.println((names.indexOf(name) + 1) + ": " + name);
});

Categories

Resources