Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cleaning a list of data in Java8

For cleaning a list of data, I have created a method which accepts the list of data and list of cleaning operation to be performed.

public <T> List<T> cleanData(List<T> data, List<Function<T, T>> cleanOps) {
    List<T>dataNew=data.stream().map((str) -> {
        T cleanData = str;
        for(Function<T,T> function:cleanOps) {
            cleanData=function.apply(cleanData);
        }
        return cleanData;
    }).collect(Collectors.toList());
    return dataNew;
}

The issue here is that we are creating the whole list again as Collectors.toList() returns a new list. Can we achieve the same result without using the extra space?

Below is the code for invocation:

public void processData() {
    List<Function<String, String>> cleanOps = new ArrayList<>();
    cleanOps.add(String::toLowerCase);
    cleanOps.add(str -> str.replaceAll(" ", ""));
    List<String> data = new ArrayList<>();
    data.add("John Doe");
    data.add("Jane Doe");
    System.out.println(Arrays.toString(cleanData(data, cleanOps).toArray()));
}
like image 252
Dharmvir Tiwari Avatar asked Dec 03 '19 10:12

Dharmvir Tiwari


People also ask

How to clean the list in Java?

The clear() method of List interface in Java is used to remove all of the elements from the List container. This method does not deleted the List container, instead it justs removes all of the elements from the List.

How do you clear a string list?

To delete a string from a string list, call the Delete method of the list, passing the index of the string you want to delete. If you do not know the index of the string you want to delete, use the IndexOf method to locate it. To delete all the strings in a string list, use the Clear method.

How do I remove a particular element from a stream list?

Using Streams, we can apply lambda functions known as Predicates. To read more about Streams and Predicates, we have another article here. Internally, removeIf uses an Iterator to iterate over the list and match the elements using the predicate. We can now remove any matching elements from the list.


1 Answers

If modifying the list in-place is allowed, you could use

public <T> List<T> cleanData(List<T> data, List<Function<T, T>> cleanOps) {
    cleanOps.stream().reduce(Function::andThen).ifPresent(f -> data.replaceAll(f::apply));
    return data;
}

andThen combines two Function instances and if at least one function was present, i.e. the cleanOps list is not empty, the resulting combined function will be applied to all list elements and the elements replaced by the result, using replaceAll.

Unfortunately, replaceAll requires a UnaryOperator<T> rather than a Function<T,T>, despite being functionally equivalent, so we have to use the adapter f::apply.

Since these function types are equivalent, we could change the list to List<UnaryOperator<T>>, but then, we have to face the fact that there is no specialized andThen implementation for UnaryOperator, so we would need:

public <T> List<T> cleanData(List<T> data, List<UnaryOperator<T>> cleanOps) {
    cleanOps.stream()
        .reduce((f1,f2) -> t -> f2.apply(f1.apply(t)))
        .ifPresent(data::replaceAll);
    return data;
}

The caller’s source changes to

List<UnaryOperator<String>> cleanOps = new ArrayList<>();
cleanOps.add(String::toLowerCase);
cleanOps.add(str -> str.replaceAll(" ", ""));
List<String> data = new ArrayList<>();
data.add("John Doe");
data.add("Jane Doe");
System.out.println(cleanData(data, cleanOps));

then.

As a side note, there is no need for a construct like

System.out.println(Arrays.toString(cleanData(data, cleanOps).toArray()));

as the toString() method of a List produces exactly the same output. Since the println(Object) method calls toString() implicitly, you can just use

System.out.println(cleanData(data, cleanOps));
like image 139
Holger Avatar answered Sep 21 '22 06:09

Holger