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()));
}
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.
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.
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.
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));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With