Reputation: 756
I would like to sort the user list based on their status but the order must be based on the order that I set.
I want to set the order of list,
The order should be 1, 0 , 5. We should also keep in mind to order the username as well.
List<User> users = new ArrayList();
users.add(new User("A", 1));
users.add(new User("B", 5));
users.add(new User("C", 0));
users.add(new User("D", 1));
users.add(new User("E", 5));
users.add(new User("F", 0));
Here's the user class
public class User {
private String username;
private Integer status;
}
It should look like this
[
{
"username": "A",
"status": 1
},
{
"username": "D",
"status": 1
},
{
"username": "C",
"status": 0
},
{
"username": "F",
"status": 0
},
{
"username": "B",
"status": 5
},
{
"username": "E",
"status": 5
}
]
I not sure if it's possible to use Comparator.comparing, since this one is neither ascending nor descending order.
Upvotes: 14
Views: 8061
Reputation: 311393
One approach could be to hold a list with the order you want and sort the users according to its index:
final List<Integer> order = Arrays.asList(1, 0, 5);
users.sort(
Comparator.comparing((User u) -> order.indexOf(u.getStatus()))
.thenComparing(User::getUsername));
Note that while this approach should be reasonable for a small number of statuses (like you currently have), it may slow down sorting if there are a large number of statuses and you need to do perform an O(n) search each time. A better performing approach (albeit arguably not as sleek), would be to use a map:
final Map<Integer, Integer> order = new HashMap<>();
order.put(1, 0);
order.put(0, 1);
order.put(5 ,2);
users.sort(Comparator.comparing((User u) -> order.get(u.getStatus()))
.thenComparing(User::getUsername));
Upvotes: 16
Reputation: 1618
As you have mentioned you need custom ordering and that means you need to define somewhere that ordering either in HashMap<Status,Rank>> or one simple way add one more attribute say Integer rank; and you can define the rank based on your ordering for status attribute like say users.add(new User("A", 1,0)); here status 1 is sortest in order and its rank=0. And then you can use Comparator on rank attribute.
For e.g :
public class User {
public String username;
public Integer status;
public Integer rank;
public User(String username, Integer status, Integer rank)
{
this.username = username;
this.status = status;
this.rank = rank;
}
}
Comparator class :
class SortByRank implements Comparator<User>
{
// Used for sorting in ascending order of
// rank number
public int compare(User a, User b)
{
return a.rank - b.rank;
}
}
Main Class :
class Main
{
public static void main (String[] args)
{
List<User> users = new ArrayList();
users.add(new User("A", 1, 0));
users.add(new User("B", 5, 2));
users.add(new User("C", 0, 1));
users.add(new User("D", 1, 0));
users.add(new User("E", 5, 2));
users.add(new User("F", 0, 1));
System.out.println("Unsorted");
for (int i=0; i<users.size(); i++)
System.out.print(users.get(i).username);
Collections.sort(users, new SortByRank());
System.out.println("\nSorted by Rank");
for (int i=0; i<users.size(); i++)
System.out.print(users.get(i).username);
}
}
Upvotes: 0
Reputation: 1209
You can try to do this step by step
//order define here
List<Integer> statusOrder= Arrays.asList(1,0,5,2);
//define sort by status
Comparator<User> byStatus = (u1, u2) -> {
return Integer.compare(statusOrder.indexOf(u1.getStatus()), statusOrder.indexOf(u2.getStatus()));
};
//define sort by name
Comparator<User> byName = Comparator.comparing(User::getUsername);
//actualy sort
users.sort(byStatus.thenComparing(byName));
Upvotes: 0
Reputation: 29700
Assuming 1
, 0
, and 5
will be the only values of status
, AJNeufeld made an excellent point in their comment; they stated that you can use an equation to map each value into an ascending order. In this case, the equation would be (x - 1)^2
where x
is the value of status
:
users.sort(Comparator.comparingDouble(user -> Math.pow(user.getStatus() - 1, 2)));
If you were to print the contents of user
after calling the above snippet, you'd get:
[User [username=A, status=1], User [username=D, status=1], User [username=C, status=0], User [username=F, status=0], User [username=B, status=5], User [username=E, status=5]]
Upvotes: 1
Reputation: 39536
If you don't mind using Guava in your project, you can use Ordering.explicit
:
users.sort(Ordering.explicit(1, 0, 5).onResultOf(User::getStatus));
If you want to sort by name also, then add thenComparing
:
users.sort(Ordering
.explicit(1, 0, 5)
.onResultOf(User::getStatus)
.thenComparing(User::getUsername));
Upvotes: 9