2

I'd like to build a Sort object using this Optional-like code:

private Sort buildSort(Collection<String> fields) {
    fields.stream()
        .map(this::buildOrder)
        .collect(Sort.unsorted(), ????, ????);
}

buildOrder is:

private Order buildOrder(String field) {
    if (field.startsWith("-")) {
        return Order.desc(field.substring(1));
    }
    else {
        return Order.asc(field);
    }
}

I need to return an Sort.unsorted() if stream is empty, or combine each Sort object.

Sort object has an and method that stands for building and concatenate orders.

Sort.by(sort1)
    .and(sort2)...

So I need that:

if (stream is empty) then
  return Sort.unsorted
else
  return foreach item in stream:
     Sort.by(item).and(item2).and(item3)...

Classes:

org.springframework.data.domain.Sort
org.springframework.data.domain.Sort.Order

NOTE

I need to use stream-api!

0

2 Answers 2

5

I believe this should work. The second map converts each Order to it's own Sort. The accumulator and combiner then just add Sorts together.

return fields.stream()
    .map(this::buildOrder)
    .map(Sort::by)
    .collect(Sort::unsorted, Sort::and, Sort::and);
2
  • I'm getting The type Sort does not define and(R, Sort) that is applicable here. Sort::and is not a BiConsumer compliant. and method is public Sort and(Sort sort) {...}
    – Jordi
    Commented Feb 18, 2019 at 10:08
  • 1
    It compiles just fine for me. I think you must have made some mistake when transcribing it. Potentially you left the first argument as Sort.unsorted() when it has actually changed to Sort::unsorted.
    – Michael
    Commented Feb 18, 2019 at 10:11
5

As you need to use Streams you can just use the this:

public Sort buildSort(Collection<String> fields) {
    return fields.stream()
        .map(this::buildOrder)
        .map(Sort::by)
        .reduce(Sort.unsorted(), Sort::and);
}

Old answer

As the buildOrder() method doesn't return an empty Sort.Order you can just initially check if fields is empty and then directly return a Sort.unsorted().

And for the combining of the sorts you may not even want to use a Stream but rather a normal loop:

public Sort buildSort(Collection<String> fields) {
    if(fields.isEmpty()) {
        return Sort.unsorted();
    }

    Sort result = null;
    for(String field : fields) {
        Sort.Order order = buildOrder(field);
        if(result == null) {
           // initially create a Sort
           result = Sort.by(order);
        } else {
            // then just concatenate them
            result = result.and(order);
        }
    }
    return result;
}
4
  • Sorry. you were right. I was using collect instead of reduce.
    – Jordi
    Commented Feb 18, 2019 at 10:12
  • @Lino since Sort.and will return a new instance, Michael’s answer will not work at all, as it never produces anything other than Sort.unsorted() as result. It’s baffling that his answer got accepted. For immutable types, reduce is the right (if not only) approach. Though, I’d use .reduce(Sort::and) .orElse(Sort.unsorted()) instead.
    – Holger
    Commented Feb 18, 2019 at 15:32
  • @Holger I am a bit baffled too, now that you mention it, thank you, I got the signatures of reduce and collect mixed up (I always do).
    – Lino
    Commented Feb 18, 2019 at 15:39
  • @Holger and I don't really like the Optional<T> reduce() method (I generally don't like Optional) but that's more an opinion than a fact ;)
    – Lino
    Commented Feb 18, 2019 at 15:39

Not the answer you're looking for? Browse other questions tagged or ask your own question.