bencampbell_14
bencampbell_14

Reputation: 607

Java sorting with multiple column conditions

I have this object ClientSearch

public class ClientSearch{
private Long clientNumber;
private String queueNumber;
private String clientName;
private String customerName;
.....
}

I need to sort this by this rule:

Show the clientNumber descending first then by customerName descending and after all records with clientNumber are displayed, sort by queueNumber in descending order and customerName in descending order.

This should be the result --- ClientNumber desc then, CustomerName desc, then, QueueNumber desc then, CustomerName desc

ClientNumber     ClientName    CustomerName
987654           Client1       Xyz1
987654           Client2       Abc2
555555           Client3       zzzzz1
21212            XYZ1          aaaab
111111           Client10      cZ
111111           Client11      cY
Z1Z1Z1           ClientX       zzzzz
Z1Z1Z1           ClientY       yyyyy
X2X2X2           Clienxyz      aaaaaa
HHHHHH           ClientH       bbbbbb
AAAAAA           ClientA       xxxxxx

Basically each ClientSearch object has either a clientNumber or a queueNumber (if there is no client number then the queuNumber is considered a client number thats why it is displayed under that column),

what I thought of doing is, after I receive a List from my query (which I have no control, I just received the List<>), i will iterate through the list with the condition.

 if clientNumber is present then add to a List<ClientSearch> withClientNumbers

 else if queueNumber is present then add to a List<ClientSearch> withQueueNumbers

After doing this, I'll sort the withClientNumbers with a Comparator that compares the ClientSearch's clientNumber then if they are equal to zero ill do another compare with ClientSearch's customerName. I just need to multiply these with -1 to make sure it is DESC.

I'll do the same with queueNumbers but with a different comparator.

then Ill create a new List newList and then will do this

newList.addAll(withClientNumbers); //make sure this is first in the list
newList.addAll(queueNumbers); // so to satisfy the condition that queueNumbers are shown after a sorted clientNumbers.

Can you suggest any other elegant way to do this? I feel that my method is not the most optimized one. Take note that I'm using Java 1.6

Upvotes: 1

Views: 1278

Answers (2)

Adrian Shum
Adrian Shum

Reputation: 40036

When it comes to sorting, it is usually just about how you are going to implement the Comparator. In such case, you simply need a Comparator that compares two ClientSearch objects with the way you described.

It will be much cleaner if you can simplify your requirement to sort by

  1. Customer Number (nulls last)
  2. Queue ID, then
  3. Customer Name

The comparator will be as easy as this, with Java 8:

import static java.util.Comparator.*;
....
Comparator<Customer> comparator = 
    comparing(Customer::getCustNumber, nullsLast(naturalOrder()))
    .thenComparing(Customer::getQueueId)
    .thenComparing(Customer::getCustName);

It is a bit different from what you originally asked for. What you asked for is

  1. If both have Customer Number, compare by
    1. Customer Number
    2. Customer Name
  2. If both does not have Customer Number, compare by
    1. Queue ID
    2. Customer Name
  3. If one has Customer Number while the other does not, the one with null Customer Number is treated as greater.

If you really need what you are looking for, it is not difficult to do. For example, you can have two Comparators each for the separate case, and combine then as one Comparator, looks something like (with aid of Java 8, should not be difficult to write one for prev Java version if you get the idea):

public class ClientSearchComparator implements Comparator<ClientSearch> {
    private static Comparator<ClientSearch> custNumberComparator = 
        Comparator.comparing(ClientSearch::getCustNumber)
                .thenComparing(ClientSearch::getCustName);
    private static Comparator<ClientSearch> queueIdComparator = 
        Comparator.comparing(ClientSearch::getQueueId)
                .thenComparing(ClientSearch::getCustName);
    @Override
    public int compare(ClientSearch c1, ClientSearch c2) {
        if (c1.getCustNumber() != null && c2.getCustNumber() != null) {
            return custIdComparator.compare(c1, c2);
        } else if (c1.getCustNumber() == null && c2.getCustNumber() == null) {
            return queueIdComparator.compare(c1, c2);
        } else if (c1.getCustNumber() != null && c2.getCustNumber() == null) {
            return -1;
        } else {  // (c1.getCustNumber() == null && c2.getCustNumber() != null) 
            return 1;
        }
    }
}

(I believe I do not need to tell you how to do sorting with an Comparator right?)


Update: As you have mentioned that you are using Java 6, here is the basic idea on what that Comparator will look like (pseudo-code):

public class ClientSearchComparator implements Comparator<ClientSearch> {
    @Override
    public int compare(ClientSearch c1, ClientSearch c2) {
        if (c1.custNum != null && c2.custNum != null) {
            if (c1.custNum != c2.custNum) {
                return c1.custNum.compareTo(c2.custNum);
            }
            return c1.custName.compareTo(c2.custName);
        } else if (c1.custNum == null && c2.custNum == null) {
            if (c1.queueId != c2.queueId) {
                return c1.queueId .compareTo(c2.queueId);
            }
            return c1.custName.compareTo(c2.custName);
        } else if (c1.custNum == null) { // c1 null && c2 not null
            return 1;
        } else {  // c1 not null && c2 null
            return -1;
        }
    }

(it will look better with some reorganization, or with aid of tools like Guava or Apache Common Langs)

Upvotes: 2

Sikorski
Sikorski

Reputation: 2691

Yours is correct way, I just tested it as below. Note optimization is done in sorting and not in comparing functions. Since you will be using Java own's sorting method you don't have to worry about optimization. Basically you are just breaking off ties of equality of clientNumber with customerNames. Here I am taking queuenumbers as clientnumbers only for simplicity. Since you are already making different lists same solution can be applied to both lists and then merge the lists. Sample code below :

public class ClientSearch{
    private String clientNumber;
   // private String queueNumber;
    private String clientName;
    private String customerName;


    public ClientSearch(String clientNumber, String clientName, String customerName) {
        this.clientNumber = clientNumber;
        //this.queueNumber = queueNumber;
        this.clientName = clientName;
        this.customerName = customerName;
    }

    public String toString(){
        return clientNumber+" "+clientName+" "+customerName;
    }

    public static void main(String[] args) {
        try {
            BufferedReader br = new BufferedReader(new FileReader("input.txt"));
            String tmp = null;
            List<ClientSearch> list = new ArrayList<>();
            while((tmp=br.readLine())!=null){
                String split [] = tmp.split(" ");
                list.add(new ClientSearch(split[0],split[1],split[2]));
            }
            System.out.println("Sorting.....");
            list.sort(new Comparator<ClientSearch>() {
                @Override
                public int compare(ClientSearch o1, ClientSearch o2) {
                    int diff = o1.clientNumber.compareTo(o2.clientNumber);
                    return diff ==0 ? o1.customerName.compareTo(o2.customerName) : diff;
                }
            });

            for (ClientSearch c : list){
                System.out.println(c);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Upvotes: 0

Related Questions