I have an unsorted list but I want to sort in a custom way i.e.
item_one_primary.pls
item_one_secondary.pls
item_one_last.pls
item_two_last.pls
item_two_primary.pls
item_two_secondary.pls
item_three_secondary.pls
item_three_last.pls
item_three_primary.pls
Here is my predefined order : primary, secondary, last
Above unordered list once the ordering is applied should look like this :
item_one_primary.pls
item_one_secondary.pls
item_one_last.pls
item_two_primary.pls
item_two_secondary.pls
item_two_last.pls
item_three_primary.pls
item_three_secondary.pls
item_three_last.pls
I tried something with comparator but I end up something like this :
item_one_primary.pls
item_two_primary.pls
item_three_primary.pls
...
Does anyone have an idea how to get this sorted?
Here is some code I've used :
List<String> predefinedOrder;
public MyComparator(String[] predefinedOrder) {
this.predefinedOrder = Arrays.asList(predefinedOrder);
}
#Override
public int compare(String item1, String item2) {
return predefinedOrder.indexOf(item1) - predefinedOrder.indexOf(item2);
}
I didn't include the splits(first split by dot(.) second split by underscore(_) to get the item in pre-ordered list).
You have to use a Comparator that checks first the item number and only if they are equal, check your predefined order.
Try something like this:
public int compare(Object o1, Object o2) {
String s1 = (String) o1;
String s2 = (String) o2;
String[] a1 = s1.split("_");
String[] a2 = s2.split("_");
/* If the primary elements of order are equal the result is
the order of the second elements of order */
if (a1[1].compareTo(a2[1]) == 0) {
return a1[2].compareTo(a2[2]);
/* If they are not equal, we just order by the primary elements */
} else {
return a1[1].compareTo(a2[1]);
}
}
This is just a basic example, some extra error checking would be nice.
A solution using the Google Guava API yields a simple and readable result:
// some values
List<String> list = Lists.newArrayList("item_one_primary", "item_one_secondary", "item_one_last");
// define an explicit ordering that uses the result of a function over the supplied list
Ordering o = Ordering.explicit("primary", "secondary", "last").onResultOf(new Function<String, String>() {
// the function splits a values by '_' and uses the last element (primary, secondary etc.)
public String apply(String input) {
return Lists.newLinkedList(Splitter.on("_").split(input)).getLast();
}
});
// the ordered result
System.out.println("o.sortedCopy(list); = " + o.sortedCopy(list));
Related
I have a collection of strings in an array like this:
ArrayList<String> collection = new ArrayList<>();
That stores:
collection: ["(,0,D=1", "(,1,D=2", "),2,D=2", "),3,D=1", "(,4,D=1", "(,5,D=2", "),6,D=2", "),7,D=1"]
I have a lot of d=1 and d=2, as you can see. How do I organize this from 1 first to 2? I tried to use a for loop but the list can contain an infinite number of d=x's. Can you help me organize?
Also, please help me so I don't change the ORDER of any numbers. Example:
collection: ["(,0,D=1", "),3,D=1", "(,4,D=1", "),7,D=1", "(,1,D=2", "),2,D=2", "(,5,D=2", "),6,D=2"]
So like, every parentheses will be aligned.
I should note that collection[0] = "(,0,D=1"
You should use a class for the items, not a string, e.g. Class Item {char c; int i; int depth;} and ArrayList. Then you can easily sort the list with a custom Comparator.
You can implement your own Comparator to do the sorting. A Comparator is a sorting algorithms that you define for your application which written in programming language. Give Collections.sort() a Comparator basically you teach Java how you want to sort the list. And it will sort the list for you.
This implementation is based on the following assumptions:
The comparison will only take effect on the first D=x pattern, subsequent will be ignored.
Element is sorted in ascending order base on x.
Elements do not have D=x will be placed at the back
class DeeEqualComparator implements Comparator<String> {
private static final String REGEX = "D=([0-9])+";
#Override
public int compare(String s1, String s2) {
// find a D=x pattern from the element
Matcher s1Matcher = Pattern.compile(REGEX).matcher(s1);
Matcher s2Matcher = Pattern.compile(REGEX).matcher(s2);
boolean s1Match = s1Matcher.find();
boolean s2Match = s2Matcher.find();
if (s1Match && s2Match) {
// if match is found on s1 and s2, return their integer comparison result
Integer i1 = Integer.parseInt(s1Matcher.group(1));
Integer i2 = Integer.parseInt(s2Matcher.group(1));
return i1.compareTo(i2);
} else if (s1Match) {
// if only s1 found a match
return -1;
} else if (s2Match) {
// if only s2 found a match
return 1;
} else {
// if no match is found on both, return their string comparison result
return s1.compareTo(s2);
}
}
Test run
public static void main(String[] args) {
String[] array = {
// provided example
"(,0,D=1", "(,1,D=2", "),2,D=2", "),3,D=1", "(,4,D=1", "(,5,D=2", "),6,D=2", "),7,D=1"
// extra test case
, "exception-5", "exception-0", "D=68" };
List<String> list = Arrays.asList(array);
Collections.sort(list, new DeeEqualComparator());
System.out.print(list);
}
output
[(,0,D=1, ),3,D=1, (,4,D=1, ),7,D=1, (,1,D=2, ),2,D=2, (,5,D=2, ),6,D=2, D=68, exception-0, exception-5]
Working with a list of Object where one of the item has an empty String. Trying to write method which would return a sorted list. By sorting means the first item value of the list should always be an empty String.
Since I don't want to manipulate the unsorted list, I am creating a new list to sort.
So far my code is:
private List<LoggerConfig> sort(List<LoggerConfig> unSortedList) {
List<LoggerConfig> sortedList = new ArrayList<LoggerConfig>(unSortedList);
//What to do here
return sortedList;
}
Looked at lot of SO posts but very confused.
You can trust the String.compareTo to match the order you seek. Here is a Comparator :
new Comparator<LoggerConfig>() {
#Override
public int compare(LoggerConfig o1, LoggerConfig o2) {
return (o1.getName().compareTo(o2.getName()));
}
};
or directly implementing Comparable in the specific class (here Dummy)
class Dummy implements Comparable<Dummy>{
String name;
public int compareTo(Dummy o) {
return this.name.compareTo(o.name);
}
}
The why :
The String.compareTo check the first characters of both to find a difference (until the smallest length of both), if they match, the lengths are use to make the difference, the longest will be after the shortest (shortest.compareTo(longuest) will return an negative value (the length difference)).
In this case, "".compareTo("abc"), there is no character in the empty String, so the first check is skipped and the length is use to compare the Strings, so an empty String will always be seen as first compare to any "non-empty" String
An example with the previous Dummy class (just need to add the Constructor Dummy(String):
public class Main {
public static void main(String[] args) {
List<Dummy> dummies = new LinkedList<Dummy>();
dummies.add(new Dummy("abc.com.core"));
dummies.add(new Dummy(""));
dummies.add(new Dummy("abc.com.core.def"));
System.out.println("BEFORE : " + dummies);
Collections.sort(dummies);
System.out.println("AFTER : " + dummies);
}
}
Output :
BEFORE : [abc.com.core, , abc.com.core.def]
AFTER : [, abc.com.core, abc.com.core.def]
You can place this condition in your comparator so that elements with an empty value are considered "less" than other elements, so that it shows up at the beginning of the sorted list. Try something like this:
Collections.sort(sortedList, new Comparator<LoggerConfig>() {
#Override
public int compare(LoggerConfig o1, LoggerConfig o2) {
if(o1.getName().isEmpty(){
return -1;
}
if(o2.getName().isEmpty(){
return 1;
}
return (o1.getName().compareTo(o2.getName()));
}
});
I didn't test this, but this should make the idea clear. If the empty element shows up at the end of the list, swap the -1 and the 1.
If your List is huge and sorting takes a lot of time, it might be a better idea to remove the empty element before sorting, then sort, then place the element at the beginning.
The Comparator solution seems feasible to me; what you're missing is implementing the compare method so that it does what you want.
Collections.sort(sortedList, new Comparator<LoggerConfig>() {
#Override
public int compare(LoggerConfig o1, LoggerConfig o2) {
if(o1.getName().equals("")){
return -1;
} else if(o2.getName().equals("")) {
return 1;
} else {
return (o1.getName().compareTo(o2.getName()));
}
}
});
As per Java docs, the Comparator has a compare method that returns an int which is
less than 0 if the first argument is less than the second
0 if the arguments are equal
greater than 0 if the first argument is greater than the second
So the Comparator you need should return the comparison of the two strings if they're both different from "", and -1 (or 1) if the first (or second) String is empty.
I have a program that calculates the correlation value between a currency and a stock value. I'm adding the "pairs" (currency name + ": " + Correlation Value) in to an ArrayList and if i print out the arraylist this is my output:
SDG: 0.6672481089755959
RON: 0.7950474904606127
MKD: 0.788195252851783
MXN: 0.8429550156320716
CAD: 0.7777753208834005
ZAR: 0.8254509631193871
I'm trying to think of a smart way to sort them by correlation value, from biggest to smallest, but can't think of a good way of doing this. Any ideas?
You could have three approaches:
Use Collections.sort with a custom comparator, which splits the string by : (and trims it) and then returns the .compareTo value of the numeric part of your string.
Create a new object maybe call it CurrencyCorrelation which has 2 properties (currencyName and correlation maybe?). The class will implement the Comparable interface and override the toString() method to yield the correlation as you'd like (currencyName + ": " + String.valueOf(correlation)). You would then call Collections.sort(...) without the need of specifying the comparator, as per option 1.
As per #Sasha Salauyou's recommendation, you could also declare the class, as per option 2 and then use Java 8 lamba expressions to define the comaparator, without the need of having your class extend the Comparable interface. This would look something like so: list.sort((e1, e2) -> e1.getCorrelation().compareTo(e2.getCorrelation()))
The second option would probably be better, with the first option requiring less changes.
You could store them in a TreeMap which is already sorted. From Docs:
The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used.
Assuming your elements are strings and that the correlation values are doubles, you can simply write your own comparator and sort:
Collections.sort(al, new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
double d1 = Double.parseDouble(s1.substring(s1.indexOf(": ") + 2));
double d2 = Double.parseDouble(s2.substring(s2.indexOf(": ") + 2));
return Double.compare(d1, d2);
}
});
/** Immutable class holding currency-correlation pair */
public static class CurrencyCor {
public final String currency;
public final Double correlation;
private CurrencyCor(String cur, Double cor) {
if (cur == null || cor == null)
throw new NullPointerException("Null argument(s)");
currency = cur;
correlation = cor;
}
#Override
public String toString() {
return String.format("%s: %s", currency, correlation)
}
}
// ...
List<CurrencyCor> list = new ArrayList<>();
list.add(new CurrencyCor("SDG", 0.6672481089755959));
list.add(new CurrencyCor("ZAR", 0.8254509631193871));
// ... add remaining pairs
list.sort((e1, e2) -> e1.correlation.compareTo(e2.correlation)); // sort by correlation value
list.forEach(System.out::println); // print out sorted pairs
Assuming I have
final Iterable<String> unsorted = asList("FOO", "BAR", "PREFA", "ZOO", "PREFZ", "PREFOO");
What can I do to transform this unsorted list into this:
[PREFZ, PREFA, BAR, FOO, PREFOO, ZOO]
(a list which begin with known values that must appears first (here "PREFA" and "PREFZ") and the rest is alphabetically sorted)
I think there are some usefull classes in guava that can make the job (Ordering, Predicates...), but I have not yet found a solution...
I would keep separate lists.
One for known values and unknown values. And sort them separately, when you need them in a one list you can just concatenate them.
knownUnsorted.addAll(unsorted.size - 1, unknonwUnsorted);
I suggest filling List with your values and using Collections.sort(...).
Something like
Collections.sort(myList, new FunkyComparator());
using this:
class FunkyComparator implements Comparator {
private static Map<String,Integer> orderedExceptions =
new HashMap<String,Integer>(){{
put("PREFZ", Integer.valueOf(1));
put("PREFA", Integer.valueOf(2));
}};
public int compare(Object o1, Object o2) {
String s1 = (String) o1;
String s2 = (String) o2;
Integer i1 = orderedExceptions.get(s1);
Integer i2 = orderedExceptions.get(s2);
if (i1 != null && i2 != null) {
return i1 - i2;
}
if (i1 != null) {
return -1;
}
if (i2 != null) {
return +1;
}
return s1.compareTo(s2);
}
}
Note: This is not the most efficient solution. It is just a simple, straightforward solution that gets the job done.
I would first use Collections.sort(list) to sort the list.
Then, I would remove the known items, and add them to the front.
String special = "PREFA";
if (list.remove(special)
list.add(0, special);
Or, if you have a list of array of these values you need in the front you could do:
String[] knownValues = {};
for (String s: knownValues) {
if (list.remove(s))
list.add(0, s);
}
Since I'm a fan of the guava lib, I wanted to find a solution using it. I don't know if it's efficient, neither if you find it as simple as others solution, but it's here:
final Iterable<String> all = asList("FOO", "BAR", "PREFA", "ZOO", "PREFOO", "PREFZ");
final List<String> mustAppearFirst = asList("PREFZ", "PREFA");
final Iterable<String> sorted =
concat(
Ordering.explicit(mustAppearFirst).sortedCopy(filter(all, in(mustAppearFirst))),
Ordering.<String>natural().sortedCopy(filter(all, not(in(mustAppearFirst)))));
You specifically mentioned guava; along with Sylvain M's answer, here's another way (more as an academic exercise and demonstration of guava's flexibility than anything else)
// List is not efficient here; for large problems, something like SkipList
// is more suitable
private static final List<String> KNOWN_INDEXES = asList("PREFZ", "PREFA");
private static final Function<Object, Integer> POSITION_IN_KNOWN_INDEXES
= new Function<Object, Integer>() {
public Integer apply(Object in) {
int index = KNOWN_INDEXES.indexOf(in);
return index == -1 ? null : index;
}
};
...
List<String> values = asList("FOO", "BAR", "PREFA", "ZOO", "PREFZ", "PREFOO");
Collections.sort(values,
Ordering.natural().nullsLast().onResultOf(POSITION_IN_KNOWN_INDEXES).compound(Ordering.natural())
);
So, in other words, sort on natural order of the Integer returned by List.indexOf(), then break ties with natural order of the object itself.
Messy, perhaps, but fun.
I would also use Collections.sort(list) but I think I would use a Comparator and within the comparator you could define your own rules, e.g.
class MyComparator implements Comparator<String> {
public int compare(String o1, String o2) {
// Now you can define the behaviour for your sorting.
// For example your special cases should always come first,
// but if it is not a special case then just use the normal string comparison.
if (o1.equals(SPECIAL_CASE)) {
// Do something special
}
// etc.
return o1.compareTo(o2);
}
}
Then sort by doing:
Collections.sort(list, new MyComparator());
I have an array of filenames and need to sort that array by the extensions of the filename. Is there an easy way to do this?
Arrays.sort(filenames, new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
// the +1 is to avoid including the '.' in the extension and to avoid exceptions
// EDIT:
// We first need to make sure that either both files or neither file
// has an extension (otherwise we'll end up comparing the extension of one
// to the start of the other, or else throwing an exception)
final int s1Dot = s1.lastIndexOf('.');
final int s2Dot = s2.lastIndexOf('.');
if ((s1Dot == -1) == (s2Dot == -1)) { // both or neither
s1 = s1.substring(s1Dot + 1);
s2 = s2.substring(s2Dot + 1);
return s1.compareTo(s2);
} else if (s1Dot == -1) { // only s2 has an extension, so s1 goes first
return -1;
} else { // only s1 has an extension, so s1 goes second
return 1;
}
}
});
For completeness: java.util.Arrays and java.util.Comparator.
If I remember correctly, the Arrays.sort(...) takes a Comparator<> that it will use to do the sorting. You can provide an implementation of it that looks at the extension part of the string.
You can implement a custom Comparator of Strings. Make it sort them by the substring after the last index of '.'. Then pass in the comparator and your array into
Arrays.sort(stringArray, yourComparator);
// An implementation of the compare method
public int compare(String o1, String o2) {
return o1.substring(o1.lastIndexOf('.')).compareTo(o2.substring(o2.lastIndexOf('.'));
}
Comparators are often hard to get exactly right, and the comparison key has to be generated for every comparison which for most sorting algorithms mean O(n log n). Another approach is to create (key, value) pairs for each item you need to sort, put them in a TreeMap, and then ask for the values as these are sorted according to the key.
For instance
import java.util.Arrays;
import java.util.TreeMap;
public class Bar {
public static void main(String[] args) {
TreeMap<String, String> m2 = new TreeMap<String, String>();
for (String string : Arrays.asList(new String[] { "#3", "#2", "#1" })) {
String key = string.substring(string.length() - 1);
String value = string;
m2.put(key, value);
}
System.out.println(m2.values());
}
}
prints out
[#1, #2, #3]
You should easily be able to adapt the key calculation to your problem.
This only calculates the key once per entry, hence O(n) - (but the sort is still O(n log n)). If the key calculation is expensive or n is large this might be quite measurable.
Create a Comparator and compare the string extensions. Take a look at the following
http://java.sun.com/j2se/1.4.2/docs/api/java/util/Comparator.html
Then pass in your List of strings to Arrays.sort(List, Comparator)
Create your own Comparator that treats the strings as filenames and compares them based on the extensions. Then use Arrays.sort with the Comparator argument.
String DELIMETER = File.separator + ".";
List<String> orginalList = new CopyOnWriteArrayList<>(Arrays.asList(listOfFileNames));
Set<String> setOfuniqueExtension = new TreeSet<>();
for (String item : listOfFileNames) {
if (item.contains(".")) {
String[] split = item.split(DELIMETER);
String temp = "." + split[split.length - 1];
setOfuniqueExtension.add(temp);
}
}
List<String> finalListOfAllFiles = new LinkedList<>();
setOfuniqueExtension.stream().forEach((s1) -> {
for (int i = 0; i < orginalList.size(); i++) {
if (orginalList.get(i).contains(s1)) {
finalListOfAllFiles.add(orginalList.get(i));
orginalList.remove(orginalList.get(i));
i--;
}
}
});
orginalList.stream().filter((s1) -> (!finalListOfAllFiles.contains(s1))).forEach((s1) -> {
finalListOfAllFiles.add(s1);
});
return finalListOfAllFiles;
If you just want to group the files by their extension and do not care about the actual alphabetical order, you can use this:
I think the simplest thing you can do that also works when the filenname does not have a "." is to just reverse the names and compare them.
Arrays.sort(ary, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
String r1 = new StringBuffer(o1).reverse().toString();
String r2 = new StringBuffer(o2).reverse().toString();
return r1.compareTo(r2);
}
});
Its a shame that java's string does not even have a reverse().