20

I need to sort a list of Person objects(List<Person>, where each Person object has few attributes like id(unique), name, age… etc).

The sorting order is based on another list. That list contains a set of Person id's (A List<String> which is already sorted).

What is the best way to order the List<Person> in the same order as the list of id's using Kotlin or Java.

Example:

List Person {
(“ID1”,”PERSON1”,22,..), (“ID-2”,”PERSON2”,20,..) ), (“ID-3”,”PERSON3”,19,..),…..
}

Ordered Id List :

List of ID {(“ID2”), (“ID1”),(”ID3”)….}

Sorted Person list should be:

List PERSON {
 (“ID-2”,”PERSON 2”,20,..) ), (“ID1”,”PERSON 2”,22,..),  (“ID-3”,”PERSON 2”,19,..),…..
}

If the Person list contains any id's which are not mentioned in the id list then those values should be at the end of the sorted list.


Edited: This is my current way in Java. I am hoping for a better way than this:

public static List<Person> getSortList(List <Person> unsortedList, List<String> orderList){

    if(unsortedList!=null && !unsortedList.isEmpty() && orderList!=null && !orderList.isEmpty()){
        List sortedList = new ArrayList<OpenHABWidget>();
        for(String id : orderList){
            Person found= getPersonIfFound(unsortedList, id); // search for the item on the list by ID
            if(found!=null)sortedList.add(found);       // if found add to sorted list
            unsortedList.remove(found);        // remove added item
        }
        sortedList.addAll(unsortedList);        // append the reaming items on the unsorted list to new sorted list
        return sortedList;
    }
    else{
        return unsortedList;
    }

}

public static Person getPersonIfFound(List <Person> list, String key){
    for(Person person : list){
        if(person.getId().equals(key)){
            return person;
        }
    }
    return null;
}
7
  • 1
    show us your code. Commented Jul 27, 2017 at 11:19
  • what have you tried already to solve your problem? Commented Jul 27, 2017 at 11:19
  • What do you mean by the 'best' way? Commented Jul 27, 2017 at 11:21
  • 1
    How big is your data sets? For trivial cases, you might as well brute force it. For each item in the id list, find the corresponding item in the person list and append to a new list. If the dataset is huge, sort the person list on ascending order of ID. Then do the same thing, but use a binary search to find person-by-id Commented Jul 27, 2017 at 11:22
  • I think the best if you do not sort it physically. Use a map of Person to store your persons. Map<Integer, Person> Then use your already sorted list of IDs as an index. Commented Jul 27, 2017 at 11:24

4 Answers 4

49

An efficient solution is to first create the mapping from the ID in the ids (your desired IDs order) to the index in that list:

val orderById = ids.withIndex().associate { (index, it) -> it.id to index }

And then sort your list of people by the order of their id in this mapping:

val sortedPeople = people.sortedBy { orderById[it.id] }

Note: if a person has an ID that is not present in the ids, they will be placed first in the list. To place them last, you can use a nullsLast comparator:

val sortedPeople = people.sortedWith(compareBy(nullsLast<String>()) { orderById[it.id] })
Sign up to request clarification or add additional context in comments.

2 Comments

nullsFirst here affects the order of null people rather than those whose orderById[id] is null. To specify null order of keys, you need to pass an optional comparator to compareBy: people.sortedWith(compareBy(nullsFirst<Int>()) { orderById[it.id] }). And that comparator is optional because without it nulls are sorted first.
do not work for me!
2

I would do something like (in pseudocode, since i don't know what your code looks like)

listOfPersons = [{2,Bob},{3,Claire},{1,Alice}]
orderList = [1,3,2]
sortedList = []
for(id in orderList)
    person = listOfPersons.lookup(id)
    sortedList.add(person)  

And the lookup would be easier if you would have a map (id-> person) rather than a listOfPersons.

Comments

1

Try the below code.

 Map<String, Person> personMap=new HashMap<>(); // create a map with key as ID and value as Person object
List<String> orderList=new ArrayList<>();  // create a list or array with ID order
List<Person> outputList=new ArrayList<>(); //list to hold sorted values

//logic

    // to sort Person based on ID list order
    for (String order : orderList) {
      if(personMap.containsKey(order)){
        outputList.add(personMap.get(order));
        personMap.remove(order);
      }
    }

  // logic to add the Person object whose id is not present in ID order list
    for (Entry<String, Person> entry : personMap.entrySet())
    {
      int lastIndex=outputList.size();
      outputList.add(lastIndex, entry.getValue());
      lastIndex++;
    }

Now, the outputList will have the values that you are expecting...

Comments

1

Following code will convert Person list to a Map where key will be the ID and value will be the Person object itself. This Map will help to quick lookup. Then iterate over the ID's list the get the value from the Map and added to a another List.

fun main(args: Array<String>) {
  // List of ID
  val listId = listOf(2, 1, 3)

  val list = listOf(Person(id = 1, name = "A"),
        Person(id = 2, name = "B"),
        Person(id = 3, name = "C"))

  val map: Map<Int, Person> = list.associateBy ({it.id}, {it})

  val sortedList = mutableListOf<Person>()

  listId.forEach({
      sortedList.add(map[it]!!)
  })

  sortedList.forEach({
     println(it)
  })
}

data class Person(val id: Int, val name: String)

Output

Person(id=2, name=B)
Person(id=1, name=A)
Person(id=3, name=C)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.