4

I have list of objects List<BoM> in BoM I have a List<BoMLine>, now I have to sort BoM list by one of BoMLine property using reduce and return a sorted List in a method,

public static List<BoM> sortBoms() {
    List<BoM> sortedBomList = new ArrayList<>();
    BoM bomn = new BoM();
    sortedBomList.add(bomList.parallelStream().reduce(bomn,
            (bom1, bom2) -> sortBoM(bom1, bom2)));
    System.out.println(sortedBomList.size());
    return sortedBomList;
}

bomList is List of BoM,and sortBoM method:

private static BoM sortBoM(BoM bom1, BoM bom2) {
    bom2.getLine().stream()
            .sorted((l1, l2) -> l1.getLineNum().compareTo(l2.getLineNum()));
    bom1 = bom2;
    return bom1;
}

BoM class:

public class BoM implements Domain {

private String BomCode;
private List<BoMLine> line = new ArrayList<BoMLine>();

public String getBomCode() {
    return BomCode;
}

public void setBomCode(String bomCode) {
    BomCode = bomCode;
}

public List<BoMLine> getLine() {
    return line;
}

public void setLine(List<BoMLine> line) {
    this.line = line;
}

public void addLine(BoMLine bomLine) {
    bomLine.setbOM(this);
    line.add(bomLine);
}}

and BoMLine class:

public class BoMLine implements Domain {

private Long lineNum;
private String material;
private BigDecimal Qty;
private BoM bOM;

public Long getLineNum() {
    return lineNum;
}

public void setLineNum(Long lineNum) {
    this.lineNum = lineNum;
}

public String getMaterial() {
    return material;
}

public void setMaterial(String material) {
    this.material = material;
}

public BigDecimal getQty() {
    return Qty;
}

public void setQty(BigDecimal qty) {
    Qty = qty;
}

public BoM getbOM() {
    return bOM;
}

public void setbOM(BoM bOM) {
    this.bOM = bOM;
}

public String getBoMCode() {
    return bOM.getBomCode();
}

@Override
public String toString() {
    return "BoMLine [ bOM=" + bOM.getBomCode() + ", lineNum=" + lineNum
            + ", material=" + material + ", Qty=" + Qty + "]";
}}

I have to order BoM list by BoMLine lineNum. but it just returns one object of bomList.Any help?

Tunaki
  • 132,869
  • 46
  • 340
  • 423
Nazila
  • 1,353
  • 4
  • 15
  • 28
  • Can you post an example of input and wanted output? I'm not sure I understand how each `BoM` should be sorted. – Tunaki Nov 03 '15 at 08:26
  • edited the post,please take a look – Nazila Nov 03 '15 at 08:46
  • I still don't understand how `BoM` objects are supposed to be sorted. You can sort the `List` by the line number but this does not help compare two `BoM` together. – Tunaki Nov 03 '15 at 08:48
  • I don''t want to compare BoMs together just sort BoMLines of each BoM and return the list – Nazila Nov 03 '15 at 08:51
  • 2
    @nazila: In your question you are saying “now I have to sort BoM list”. If that’s not the list you have to sort, then phrase your question accordingly. – Holger Nov 03 '15 at 09:23
  • You're right I have to edit the question – Nazila Nov 03 '15 at 11:59

3 Answers3

2

You can sort each BoMLine in ascending order of the line number by creating a custom Comparator using Comparator.comparing:

List<BoM> sortedBomList = new ArrayList<>();
sortedBomList.forEach(bom -> bom.getLine().sort(comparing(BoMLine::getLineNum)));

Note that this will mutate the List<BoMLine> and List<BoM> which might not be a good idea.

A better way would be to go for immutability and create a constructor taking the bom code and bom line list:

List<BoM> sortedBomList = 
        bomList.stream()
               .map(bom -> new BoM(
                               bom.BomCode,
                               bom.getLine().stream()
                                            .sorted(comparing(BoMLine::getLineNum))
                                            .collect(toList())
                           )
               )
               .collect(toList());
Tunaki
  • 132,869
  • 46
  • 340
  • 423
  • 1
    @nazila What do you mean, you "must use reduce"? You can't sort a list using reduce, that's not what this method is for. What do you really want here? – Tunaki Nov 03 '15 at 10:17
  • you can implement all of these methods with reduce but i don't know how – Nazila Nov 03 '15 at 10:19
  • 2
    @nazila Do you know what the method `reduce` does? It reduces (or collects) a list of element into a single element. For example: sum all values of a list (this reduces a list of number into a single result: the sum of all elements). This does not sort. – Tunaki Nov 03 '15 at 10:22
0

You would not be able to get a sorted list with reduce. For getting a sorted list back you would have to use sorted in sortBoms() method followed by a collect logic that returns a list.

The modified code would look like the snippet below:

sortedBomList.add(bomList.parallelStream().sorted(byBoMLineProperty).collect(Collectors.toList());

This would also entail implementing byBoMLineProperty comparator that compares two BoM by its BoMLine property.

-2

You have drastically overcomplicated your question by posting a lot of irrelevant code. What you are asking about is how to sort a list of numbers using reduce, so the entire problem can be effectively simplified to implementing method List<Integer> sorted(List<Integer> list).

Please note that although this is possible, it is inefficient and solution shown by Tunaki is recommended. I guess this is part of some university assignment.

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;

import static java.util.Arrays.asList;
import static java.util.Collections.singleton;
import static junit.framework.Assert.assertEquals;

public class ReductionSortingTest {

    @Test
    public void sortsTheList() {
        List<Integer> list =     asList(5,  3, 9, 8, 15, -4, 9);
        List<Integer> expected = asList(-4, 3, 5, 8, 9,  9,  15);

        List<Integer> sorted = sorted(list);

        assertEquals(expected, sorted);
    }

    private static List<Integer> sorted(List<Integer> list) {
        PriorityQueue<Integer> pq = list.stream()
                .map((Integer n) -> new PriorityQueue<>(singleton(n)))
                .reduce(new PriorityQueue<>(), (pq1, pq2) -> {
                    pq1.addAll(pq2);
                    return pq1;
                });

        List<Integer> result = new ArrayList<>();
        while (!pq.isEmpty()) {
            result.add(pq.poll());
        }
        return result;
    }

}

Java lacks a sorted list so I decided to go with PriorityQueue, read more about it in this question. PriorityQueue guarantees that the top element (accessed via peek(), poll() methods) is the lowest according to the natural ordering of elements. Order of elements returned by its iterator is not guaranteed, hence why we have to empty the queue using poll() method - you might want to implement your own SortedList class instead.

EDIT:

Here is a sorted list solution. Keep in mind that although there is no final iteration and it uses binary search, its efficiency is far from simple sort (we are talking probably about O(n^3)).

private List<Integer> sorted(List<Integer> list) {
    return list.stream()
            .map(SortedIntList::new)
            .reduce(new SortedIntList(), (a, b) -> {
                a.addAll(b);
                return a;
            });
}

private static class SortedIntList extends ArrayList<Integer> {

    public SortedIntList() {}

    public SortedIntList(int element) {
        super(singletonList(element));
    }

    @Override
    public boolean add(Integer integer) {
        int insertionPoint = Collections.binarySearch(this, integer);
        if (insertionPoint < 0) {
            insertionPoint = -insertionPoint - 1;
        }
        super.add(insertionPoint, integer);
        return true;
    }

    @Override
    public boolean addAll(Collection<? extends Integer> c) {
        c.forEach(this::add);
        return true;
    }

}
Community
  • 1
  • 1
Jaroslaw Pawlak
  • 5,538
  • 7
  • 30
  • 57
  • 2
    You are not sorting using `reduce`, you are just adding elements to an intrinsically sorted collection, which is, by the way, an invalid use of `reduce` as it’s mutating the input values. – Holger Nov 03 '15 at 11:38
  • @Holger 1. As far as I understood the question, the task is to sort using `stream().reduce(...)` instead of `stream().sorted()` - we all know it doesn't make much sense, Tunaki explained it in detail. 2. The way I used `reduce` is absolutely valid. I decided to mutate input values instead of creating new collections every time because of efficiency, however all these priority queues are used only locally inside this method. – Jaroslaw Pawlak Nov 03 '15 at 11:43
  • 3
    Maybe you want to read [the documentation](http://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#Reduction) first and understand the difference between “Reduction” and “Mutable Reduction”. For the latter, there’s a dedicated method, `collect` to be used instead of abusing `reduce`. You are not the one to decide whether a use is “absolutely valid”, it’s up to the API designers to decide what’s a correct usage and they already did. – Holger Nov 03 '15 at 11:49
  • @Holger I think you've made your point. If you'd like to demonstrate the 'right' way, please post an answer. Your comments, while not out of bounds, are not useful for future visitors because it requires them to get around the argument you're having with the OP. – George Stocker Nov 03 '15 at 14:12