Sort and dedupe java collections - java

I want to achieve the following, I have a collection of dates in a list form which I want deduped and sorted. I'm using collections.sort to sort the list in ascending date order and then using a treeSet to copy and dedupe elements from the list. This is a 2 shot approach ? Is there a faster, 1 step approach ?
EDIT::
Metadata
{
String name;
Date sourceDate;
}
Basically I want to order Metadata object based on the sourceDate and dedupe it too.

You can skip the Collections#sort step: TreeSet will remove duplicates and sort the entries. So basically it is a one line operation:
Set<Date> sortedWithoutDupes = new TreeSet<Date> (yourList);
If the Date is a field in your object, you can either:
have your object implement Comparable and compare objects based on their date
or pass a Comparator<YourObject> as an argument to the TreeSet constructor, that sorts your objects by date
In both cases, you don't need to pre-sort your list.
IMPORTANT NOTE:
TreeSet uses compareTo to compare keys. So if 2 keys have the same date but different names, you should make sure that your compare or compareTo method returns a non-0 value, otherwise the 2 objects will be considered equal and only one will be inserted.
EDIT
The code could look like this (not tested + you should handle nulls):
Comparator<Metadata> comparator = new Comparator<Metadata>() {
#Override
public int compare(Metadata o1, Metadata o2) {
if (o1.sourceDate.equals(o2.sourceDate)) {
return o1.name.compareTo(o2.name);
} else {
return o1.sourceDate.compareTo(o2.sourceDate);
}
}
};
Set<Metadata> sortedWithoutDupes = new TreeSet<Metadata> (comparator);
sortedWithoutDupes.addAll(yourList);

TreeSet will automatically sort its elements, so you shouldn't need to sort the list before adding to the set.

Related

Sort list of objects based on a parameter value in Java

I have list of objects and structure of the object is defined below:
class PredicateInfo {
String predicateName;
String predicateStatus;
}
Here, predicateName can be any valid string and predicateStatus can be any one of the string from these values: VERIFIED, IN_PROGRESS, UNVERIFIED, NOT_INITIATED.
Priority of these strings:
Priority 1: VERIFIED
Priority 2: IN_PROGRESS
Priority 3: UNVERIFIED
Priority 4: NOT_INITIATED
Here I have a use case where I want to sort List[PredicateInfo] based on the predicateStatus.
For ex:
Input list:
List[ PredicateInfo("A", "IN_PROGRESS"), PredicateInfo("A", "VERIFIED")]
Output:
List[ PredicateInfo("A", "VERIFIED"), PredicateInfo("A", "IN_PROGRESS")]
One simple solutions is to iterate over and over to get the sorted list, I am trying to find other alternatives to achieve the same.
Use Java's Comparator to achieve this:
public Comparator<PredicateInfo> PredicateInfoComparator
= new Comparator<PredicateInfo>() {
public int compare(PredicateInfo info1, PredicateInfo info2) {
//your sorting logic here
}
};
And calling it using:
Collections.sort(list, new PredicateInfoComparator());
As explained by Javadoc, the sorting logic should return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second. See here for complete Javadoc.
Alternatively, PredicateInfo can implement Comparable interface and you can call sorting calling:
Collections.sort(list);
which will implicitly call the method compareTo declared in Comparable. More details here.
You can pass a comparator to the sort method.
List<String> predicateStatuses = new ArrayList<>();
predicateStatuses.add("VERIFIED");
predicateStatuses.add("IN_PROGRESS");
predicateStatuses.add("UNVERIFIED");
predicateStatuses.add("NOT_INITIATED");
predicateInfos.sort(Comparator.<PredicateInfo>comparingInt(predicateInfo -> predicateStatuses.indexOf(predicateInfo.getPredicateStatus()))
.thenComparing(PredicateInfo::getPredicateName));
The logic of the comparator function is:
First, sort by the position of the predicateStatus string in the predicateStatuses list. This is the order (or priority) you have provided. So, the PredicateInfo object with predicateStatus = VERIFIED will come first in the output.
Next, for objects with same predicateStatus sort by the natural ordering (lexicographic) of the predicateName
Ideone Demo
For values in a list :
Map<Integer, String> priorityMap = new HashMap<>();
priorityMap.put(1, "VERIFIED");
priorityMap.put(2, "IN_PROGRESS");
priorityMap.put(3, "UNVERIFIED");
priorityMap.put(4, "NOT_INITIATED");
Collections.sort(inputList, new Comparator<PredicateInfo >() {
#Override
public int compare(PredicateInfo obj1, PredicateInfo obj2) {
return priorityMap.get(obj1.getPredicateStatus()) - priorityMap.get(obj2.getPredicateStatus())
}
});
This will give you a sorted list

Sort List<Object[]> after three objects

I have a list List<Object[]>.
The Object[] has the following objects:
Date "dd.MM.yyyy"
String
Date "HH:mm".
I am able to sort the list after the dd.MM.yyyy and then after the String. Now I want to sort it after the time too also the result should be sorted "dd.MM.yyyy", sorted "String", sorted "time".
Collections.sort(resultList, new Comparator<Object[]>() {
public int compare(Object[] o1, Object[] o2) {
int dateComparison = (( o2[0]).compareTo(o1[0]));
return dateComparison == 0 ? ( o1[1]).compareTo( o2[1]) : dateComparison;
}
});
How can I get it to work?
Your problem is: wrong abstraction.
When you have "data" that belongs together, then you turn that into a class.
Meaning: instead of keeping three lists with data, you create a class that reasonably "wraps" around these three pieces of information. And then you create an array/list of that class.
Because then you can easily create different Comparator objects that compare objects of that class - by looking at different fields for example.
In other words: your are (more or less) writing "procedural" code with flat data, and "outside" code that "combines" that flat data. But when using a OO language such as Java, you should instead strive to create helpful abstractions.
But in case you want to go on with your approach, have a look at this pseudo code:
int dateComparison = o2[0].compareTo(o1[0]);
if (dateComparison != 0) return dateComparison;
int stringComparison = o2[1].compareTo(o1[1]);
if (stringComparison != 0) return stringComparison;
int secondDateComparison = o2[2].compareTo(o1[2]);
return dateComparison;
( obviously you need some casts here and there, but as your input isn't using them I left that out as well )
Try this.
resultList.sort(
Comparator.comparing((Object[] a) -> (Date)a[0])
.thenComparing(a -> (String)a[1])
.thenComparing(a -> (Date)a[2]));
Merge your Date and Time objects into a java.util.Date, then use your String as the key in a HashMap. From here, you can sort the List<HashMap<String, Date> by getting the Date first, then comparing the String keys using compareTo().
Just as a side note: if you are using a List of Arrays or a List of Lists, then it's likely a code smell.

Frequency of a value in a list of entities

I have an entity named Elementfisa, which contains as values (id,Post,Sarcina). Now, Post(Int Id,String Nume,String Tip) and Sarcina(Int Id,String Desc) are also entities. I have a List of all the elements I added as Elementfisa, and I want to get in a separate list the frequency of every Sarcina that every Elementfisa contains. This is my code right now:
int nr=0;
List<Integer> frecv=new ArrayList<Integer>();
List<Sarcina> sarcini = new ArrayList<>();
List<Elementfisa> efuri=findEFAll();
for (Elementfisa i : efuri)
{
nr=0;
for (Sarcina s : sarcini)
if (s.equals(i.getSarcina()))
nr=1;
if (nr==0)
{
int freq = Collections.frequency(efuri, i.getSarcina());
sarcini.add(i.getSarcina());
frecv.add(freq);
}
}
(findEFAll() returns every element contained in a Hashmap from a repository)
But for some reason, while the sarcini list contains all the Sarcina from every Elementfisa, the frequency list will show 0 on every position. What should I change so every position should show the correct number of occurrences?
You're using Collections.frequency() on efuri, a List<Elementfisa>. But you're passing i.getSarcina() to it, a Sarcina object. A List of Elementfisa cannot possibly contain a Sarcina object, so you get zero. You may have passed the wrong list to the method.
Edit:
To look at all Sarcinas in efuri, you can do this using Java 8 streams:
efuri.stream().map(element -> element.getSarcina())
.collect(Collectors.toList()).contains(i.getSarcina())
Breakdown:
efuri.stream() //Turns this into a stream of Elementfisa
.map(element -> element.getSarcina()) //Turns this into a stream of Sarcina
.collect(Collectors.toList()) //Turn this into a list
.contains(i.getSarcina()) //Check if the list contains the Sarcina
Are you sure you do not need to override equals() of Elementisa? (and hashcode() too). The default Java equals() does not seem to get what you want because it would be checking the identity (not the value) of two Elementisa objects, while in your logic, two such objects with the same values may be considered as equivalent.
For more information on equals(), see
What issues should be considered when overriding equals and hashCode in Java?

Collections Sort to sort both ArrayLists the same

My program has to use the Collections sort method to sort the ArrayList of Strings lexicographically but each String has a corresponding integer value stored in a separate ArrayList. I want to sort them both the same so the integer values stay with the correct Strings. And if you know a better way to store both values I'm all ears.
public class a5p1b {
public static void main(String[] args) {
Scanner input = new Scanner(System.in).useDelimiter("[^a-zA-z]+");
// ArrayLists to store the Strings and the frequencies
ArrayList<String> lst = new ArrayList<String>();
ArrayList<Integer> intLst = new ArrayList<Integer>();
//loops through as long as there is user input
while (input.hasNext()) {
String str = input.next().toLowerCase();
// if the list already has the string it doesn't add it and it
// ups the count by 1
if (lst.contains(str)) {
int index = lst.indexOf(str);
intLst.set(index, intLst.get(index) + 1);
} else {
// if the word hasnt been found yet it adds it to the list
lst.add(str);
intLst.add(1);
}
}
}
}
You are getting your abstractions wrong. If that string and that number belong together, then do not keep them in two distinct lists.
Instead create a class (or maybe use one of the existing Pair classes) that holds those two values. You can then provide an equals method for that class; plus a specific comparator, that only compares the string elements.
Finally, you put objects of that class into a single list; and then you sort that list.
The whole idea of good OO programming is to create helpful abstractions!
For the record: as dnault suggests, if there is really no "tight" coupling between strings and numbers you could also use a TreeMap (to be used as TreeMap<String, Integer>) to take care of sorting strings that have a number with them.
Try
inList.sort(Comparator.comparing(i -> i.toString());
Although, I don't think the two lists is a good idea.
You should use a Map to associate each unique String key with an Integer value.
Then you can invoke Collections.sort on the map's set of keys returned by keySet().
Additionally, if you use a SortedMap such as TreeMap, it is not necessary to sort the keys. However that solution may not fulfill the requirements of your "Assignment 5 Problem 1b."

Sorting ArrayList data within table

I have a TreeTable and would like to perform the sort by number and alphabetically when clicking on header.
Example:
On a first click, I have to check that the column content is sorted by number
If I click on another column that contains String data, I have to check that column content is sorted alphabetically.
Are there known functions that could I use?
I've used Collections for sorting number , but how do I can make the sort alphabetically ?
Collections.sor(myList) is OK for sorting by number but I would sort data alphabetically.
thanks
This can easily be done via Collections.sort(...). Create a copy of your list, sort it and check if they are equal.
Example:
List <String> copy = new ArrayList <String>(original);
Collections.sort(copy);
assertEquals(copy, original);
This can be done, if the elements in the list are comparable (i.e. are of type T implements Comparable <T>). Strings are comparable, and their default comparator sorts them alphabetically (though upper-case are always comes before lower-case)
You may also provide a Comparator for a more flexible sorting.
Here is a more complicated example.
List <String> unholyBible = new ArrayList <String>();
unholyBible.add("armageddon");
unholyBible.add("abyss");
unholyBible.add("Abaddon");
unholyBible.add("Antichrist");
Collections.sort(unholyBible);
System.out.println(unholyBible);
This will print us [Abaddon, Antichrist, abyss, armageddon]. This is because default comparation is case-sensitive. Lets fix it:
List <String> unholyBible = new ArrayList <String>();
unholyBible.add("armageddon");
unholyBible.add("abyss");
unholyBible.add("Abaddon");
unholyBible.add("Antichrist");
Collections.sort(unholyBible, new Comparator <String>() {
public int compare(String o1, String o2){
return o1.compareToIgnoreCase(o2);
}
});
System.out.println(unholyBible);
This one prints [Abaddon, abyss, Antichrist, armageddon].
Now you may worship Satan in strict alphabetical order.
See also
Comparator API
Collections.sort(List, Comparator)

Categories

Resources