Say I have an array of strings:
String[] array = {
"2183417234 somerandomtexthere",
"1234123656 somemorerandomtexthere",
"1093241066 andevenmore",
"1243981234 you get what i mean",
//etc
};
How would I sort this array using the long (it's a long) at the start of the string, so it'll end up looking like this:
String[] array = {
"1093241066 andevenmore",
"1234123656 somemorerandomtexthere",
"1243981234 you get what i mean",
"2183417234 somerandomtexthere",
//etc
};
I've tried everyting from making it an arraylist and using Collections#sort to creating my own comparator, to using a sorted map / tree map and I just can't figure it out.
Thanks.
Use this function:
static long comparedValue(String s) {
return Long.valueOf(s.substring(0, s.indexOf(' ')));
}
and then define a Comparator in terms of it:
public int compare(String left, String right) {
return comparedValue(left) - comparedValue(right);
}
Using Google Guava:
List<String> unsorted = Arrays.asList(array);
Function<String, Long> longFunction = new Function<String, Long>() {
#Override public Long apply(String input) {
return Long.valueOf(input.split(" ")[0]);
}
};
List<String> sorted = Ordering.natural().onResultOf(longFunction).immutableSortedCopy(unsorted);
Or if you don't wanna use a List (you should always prefer collections to arrays):
Arrays.sort(array, Ordering.natural().onResultOf(longFunction));
The input that you have shown works perfectly fine. But that's because all of them have the same number of digits.
public static void main(String[] args) {
String[] array = { "2183417234 somerandomtexthere",
"1234123656 somemorerandomtexthere", "1093241066 andevenmore",
"1243981234 you get what i mean", "999 little shorter"
// etc
};
List<String> list = Arrays.asList(array);
Collections.sort(list);
System.out.println(list);
}
Problems start to occur, when you use some shorter numbers - as 999 shown above...
output will be:
[1093241066 andevenmore, 1234123656 somemorerandomtexthere, 1243981234 you get what i mean, 2183417234 somerandomtexthere, 999 little shorter]
So, to make it working allways - you need your custom comparator, that will be able to split given Strings, and then take the number part out of them, and compare them. Using #Marko Topolik solution:
static long comparedValue(String s) {
return Long.valueOf(s.substring(0, s.indexOf(' ')));
}
public int compare(String left, String right) {
long result = comparedValue(left) - comparedValue(right);
boolean numberPartAreEqual = result == 0;
if (numberPartAreEqual) {
result = left.compareTo(right);
}
return (int) result;
}
A custom comparator should work fine:
public class LongPrefixComparator implements Comparator<String> {
#Override
public int compare(String s1, String s2) {
final long pref1 = getPrefixValue(s1);
final long pref2 = getPrefixValue(s2);
return s1 == s2 ? 0 : s1 < s2 ? -1 : 1;
}
private static long getPrefixValue(String stg) {
int len = stg.indexOf(' ');
if (len > 0) {
try {
return Long.parseLong(stg.substring(0, len));
catch (NumberFormatException ignored) {}
}
return 0L;
}
}
Related
ArrayList<String> heights = new ArrayList<String>();
Comparator<String> setTableNumber = new Comparator<String>(){
#Override
public int compare(String o1, String o2){{
return (o1).compareTo(o2);}}};
Collections.sort(players, setTableNumber);
Suppose heights consists Strings that are like this ["5'11"", "6'11"","4'2""]
And I want it to sort from highest to lowest like ["6'11"","5'11"","4'2""]
I'm thinking about using .split('), converting the strings to integers and then multiplying the feet by 12 and then adding that to the second string (after I trim off the ") and comparing two values as inches. But is there a better way to do it?
Thanks in advance.
Personally, I'd convert the String values to int (inches) up-front, this gives you the opportunity to deal with possible errors during conversation.
ArrayList<String> heights = new ArrayList<String>();
heights.add("5'11");
heights.add("6'11");
heights.add("5'2");
heights.add("4'2");
Comparator<String> setTableNumber = new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
Integer lhs = convertToInches(o1);
Integer rhs = convertToInches(o2);
if (lhs == null) {
return 1;
} else if (rhs == null) {
return -1;
}
return lhs.compareTo(rhs);
}
protected Integer convertToInches(String value) {
String parts[] = value.split("'");
if (parts.length != 2) {
return null;
}
return (Integer.parseInt(parts[0]) * 12) + Integer.parseInt(parts[1]);
}
};
Collections.sort(heights, setTableNumber);
System.out.println(heights);
which prints
[4'2, 5'2, 5'11, 6'11]
This workflow will place "bad" values to the end of the list, for example...
ArrayList<String> heights = new ArrayList<String>();
heights.add("Worse");
heights.add("5'11");
heights.add("6'11");
heights.add("5'2");
heights.add("Bad");
heights.add("4'2");
will generate
[4'2, 5'2, 5'11, 6'11, Worse, Bad]
But personally, I'd deal with this before trying to sort the list (by converting the values to int). You can then do some additional formatting to the int values when you want to print it
It is inefficient to do a lot of things each time you compare.
Try this.
public static void main(String[] args) {
List<String> heights = List.of("5'11\"", "6'11\"","4'2\"");
List<String> sorted = heights.stream()
.map(s -> new Object() {
String feetInch = s;
int inch;
{
String[] f = feetInch.replaceFirst("\"", "").split("'");
inch = Integer.parseInt(f[0]) * 12 + Integer.parseInt(f[1]);
}
})
.sorted(Collections.reverseOrder(Comparator.comparing(obj -> obj.inch)))
.map(obj -> obj.feetInch)
.toList();
System.out.println(sorted);
}
output:
[6'11", 5'11", 4'2"]
I'm trying to sort an ArrayList which a series of String composed as well (XX and YY are numbers):
Test: XX Genere: Maschio Eta: YY Protocollo: A
I would link to sort them by only considering the YY value. I found this method but it considerers all the digits of the string and I cannot remove n digits before YY because I don't know from how many digits is composed XX:
Collections.sort(strings, new Comparator<String>() {
public int compare(String o1, String o2) {
return extractInt(o1) - extractInt(o2);
}
int extractInt(String s) {
String num = s.replaceAll("\\D", "");
// return 0 if no digits found
return num.isEmpty() ? 0 : Integer.parseInt(num);
}
});
You also need to come up with how you'd like to sort the strings that don't have a number in the second part.
Collections.sort(strings, new Comparator<String>() {
public int compare(String o1, String o2) {
return Comparator.comparingInt(this::extractInt)
.thenComparing(Comparator.naturalOrder())
.compare(o1, o2);
}
private int extractInt(String s) {
try {
return Integer.parseInt(s.split(":")[1].trim());
}
catch (NumberFormatException exception) {
// if the given String has no number in the second part,
// I treat such Strings equally, and compare them naturally later
return -1;
}
}
});
UPDATE
If you are sure Integer.parseInt(s.split(":")[1].trim()) never fails with an exception, Comparator.comparingInt(this::extractInt) would be enough, and you could go with a shorter comparator.
Collections.sort(strings,
Comparator.comparingInt(s -> Integer.parseInt(s.split(":")[1].trim())));
YY seems to be the second number in the string, so you can extract it with the regex \d+ and calling Matcher.find() twice.
// assuming extractInt is declared in "YourClass"
static int extractInt(String s) {
Matcher m = Pattern.compile("\\d+").matcher(s);
m.find();
if (m.find()) {
return Integer.parserInt(m.group());
} else {
return 0;
}
}
// ...
Collections.sort(strings, Comparator.comparingInt(YourClass::extractInt));
You should also consider the approach of first parsing all the strings into a list of instances of a class like this:
class MyObject {
private int test;
private String genere;
private int eta;
private int protocollo;
// getters and constructor...
}
And then you can simply use Comparator.comparingInt(MyObject::getEta) as the comparator.
One way to compare them is to find the substring "Eta: " and compare the strings from it's position:
Collections.sort(strings, (String s1, String s2) -> s1.substring((s1.indexOf("Eta: "))).
compareTo(s2.substring((s2.indexOf("Eta: ")))));
I'd like to count all keys in a HashMap which begin with a given number.
The size of each key is not always the same.
Example:
given number(long):
long l = 9988776655
find the keys (long) which begin with that number like:
9988776655xxxxxxxxxxxxxxx
in which x stands for any integer.
How do I approach this problem? Since the length of the keys is not always the same I cannot do it with multiple modulo operations. (or can I?)
I'd just convert the keys to strings:
public static long keysStartingWith(Map<Long, ?> map, long toSearch) {
String searchStr = String.valueOf(toSearch);
return map.keySet().stream().filter(k -> k.toString().startsWith(searchStr)).count();
}
Try converting the long l and the keys to strings. Then compare the beginning of the strings. Something like this:
long l = 1234L;
Map<Long, Object> hashMap = new HashMap<>();
hashMap.put(1234567L, 1);
hashMap.put(1334567L, 2);
String longString = ""+l;
for(Map.Entry entry: hashMap.entrySet()) {
String keyString = ""+entry.getKey();
if(keyString.startsWith(longString)) {
System.out.println(entry.getValue());
}
}
You could implement a general method, like this:
public static int numberOfStartsWith(Map mp, String start) {
int count = 0;
for (String key : map.keySet()) {
if (key.startsWith(start) count++;
}
return count;
}
and overload it, like this:
public static int numberOfStartsWith(Map mp, int start) {
return MyClass.numberOfStartsWith(mp, String.valueOf(start));
}
and
public static int numberOfStartsWith(Map mp, long start) {
return MyClass.numberOfStartsWith(mp, String.valueOf(start));
}
I have a big string list of names and an autofill function for a combobox.
Currently i'm just filtering according to contains.
But the order in the combobox isn't nice.
Is there a easy way to sort the string list (after contains) according to equality to a string?
Example:
Currently:
combobox input -> Neustadt
List<String> 1. Bad Neustadt an der Saale
2. Bremen-Neustadt
3. ...
n. Neustadt
n+1. Neustadt / Pelzerhaken
Wandted:
1. Neustadt
2. Neustadt / Pelzerhaken
3. ...
n. Bremen-Neustadt
EDIT
Based on Petr Gladkikh's answer my solution is following method:
public static List<String> sortListCompairedToEquality(List<String> list, String str) {
if (list == null || str == null)
return null;
final String compStr = str.toLowerCase();
Collections.sort(list, new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
s1 = s1.toLowerCase();
s2 = s2.toLowerCase();
if (s1.equals(compStr) && !s2.equals(compStr)) {
return -1;
} else if (!s1.equals(compStr) && s2.equals(compStr)) {
return 1;
} else if (s1.indexOf(compStr) < s2.indexOf(compStr)) {
return -1;
} else if (s1.indexOf(compStr) > s2.indexOf(compStr)) {
return 1;
}
return 0;
}
});
return list;
}
It is not clear what rules of "equality to a string" you have in mind. Generally if you want to sort according to some rules use 'sort' method with custom comparator:
java.util.Collections.sort(listOfStrings, new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
return 0; // Your comparison logic goes here
}
});
See javadoc for java.util.Comparator interface for details.
Is there an easy way of finding the MAX number from the list where number is stored in x.y.z format? e.g. To manage some system versions.
I have tried Collection.max(list) and that does not work.
Sample Code:
public static void main(String args[])
{
List<String> list = new ArrayList<String>();
list.add("1.0.0");
list.add("1.1.0");
list.add("1.9.0");
list.add("1.10.0");
System.out.println(Collections.max(list));
}
Expected: 1.10.0
Result: 1.9
Thanks for your time.
Try to use this one :
Collections.max(myList, new Comparator<String>() {
#Override
public int compare(String lhs, String rhs) {
String[] first = lhs.split("\\.");
String[] second = rhs.split("\\.");
for (int i = 0; i < first.length; i++) {
if(Integer.valueOf(first[i]) > Integer.valueOf(second[i])) {
return 1;
}
if(Integer.valueOf(first[i]) < Integer.valueOf(second[i])) {
return -1;
}
}
return 0;
}
});
Well for one thing, you need to ensure that Java knows they are numbers - at the moment they're just Strings, and strings sort lexigraphically (i.e. in "alphabetical order").
My approach to this would be to create a small class that implements Comparable, which will then work automatically with sorting and comparison logic. Something like this perhaps:
public class VersionNumber implements Comparable<VersionNumber> {
public final int major;
public final int minor;
public final int patch;
// Constructor etc. elided
public int compareTo(VersionNumber other) {
if (other.major != major) return major - other.major;
if (other.minor != minor) return minor - other.minor;
return patch - other.patch;
}
}
Parsing the string to create instances of this class is left as an exercise to the reader!
You may have to write a custom Comparator for comparing version number strings:
public class VersionComparator extends Comparator<String> {
#Override
public int compare(String o1, String o2) {
// Get major/minor/revison numbers by splitting strings at dots
String[] p1 = o1.split("\\.");
String[] p2 = o2.split("\\.");
// Compare major versions then minor then revision until a difference found
for(int i = 0; i < (p1.length < p2.length) ? p1.length : p2.length; i++) {
int result = Integer.valueOf(p1[i]).compareTo(Integer.valueOf(p2[i]));
if(result != 0) return result;
}
// Return zero if they're identical
return 0;
}
}
The you can use this comparator with the Collections.max function:
Collections.max(list, new VarsionComparator());
You can use version of max with the specified comparator:
System.out.println(Collections.max(list, new Comparator<String>() {
public int compare(String s1, String s2)
{
StringTokenizer st1 = new StringTokenizer(s1,".");
StringTokenizer st2 = new StringTokenizer(s2,".");
int res = 0;
String t1, t2;
while(st1.hasMoreTokens() && st2.hasMoreTokens())
{
t1 = st1.nextToken();
t2 = st2.nextToken();
res = Integer.valueOf(t1).compareTo(Integer.valueOf(t2));
}
if(res == 0)
{
res = st1.hasMoreTokens() ? 1 : (st2.hasMoreTokens() ? -1 : 0);
}
return res;
}
public boolean equals(Object obj) { return false; }
}));
This will give you 1.9 because it will not consider second number to be 10, it will treat it as 1 first and then 9
Edit
If you want to do it manually, then
Split your number on basis of "."
Check manually which number is greater.