Sort String ArrayList Numerically in Java Android - java

I have an ArrayList as defined below:
List<String> where = new ArrayList<String>();
where.add("1 Kg");
where.add("500 gram");
where.add("5 Kg");
When I display this list, values shown are shown as:
1 Kg
500 gram
5 Kg
I want it to be displayed as given below:
500 gram
1 Kg
5 Kg
How should I sort it.

You need a Comparator where you can write your comparison logic.
See the following implementation.
package test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<String> where = new ArrayList<String>();
where.add("5 Kg");
where.add("500 gram");
where.add("1 Kg");
Collections.sort(where, new MassComparator());
for(String mass : where) {
System.out.println(mass);
}
}
public static class MassComparator implements Comparator<String> {
#Override
public int compare(String weight1, String weight2) {
double val1 = Double.parseDouble(weight1.replace("gram", "").replace("Kg", "").trim());
double val2 = Double.parseDouble(weight2.replace("gram", "").replace("Kg", "").trim());
if (weight1.contains("gram")) {
val1 *= .001;
}
if (weight2.contains("gram")) {
val2 *= .001;
}
int result = 0;
if (val1 < val2) {
result = -1;
} else if (val1 > val2) {
result = 1;
}
return result;
}
}
}
Input
List<String> where = new ArrayList<String>();
where.add("500 gram");
where.add("2 Kg");
Output
500 gram
2 Kg

If you really want to sort String representations of values instead of harmonizing the values in your data (e.g. all in grams) and sort them in the natural order of the values only, you'll need quite some work.
Use a Comparator<String> - see here for API. You'll inject this in an invocation of Collections.sort when sorting your List (API here).
In your compare method, parse two things out of each String through regular expressions: a number (real or integer, depends on your data) and a measurement unit (possibly based on an map defining all variants for gram, kilogram, etc.)
Then compare the measurement units (possibly using another Comparator<String>!) and convert the parsed numbers to their values for a single unit (likely grams, etc.)
Finally compare the harmonized numerical values after converting them to actual numbers (e.g. Integers or Doubles, etc.) - using their natural order this time
"Optionally", handle all edge cases: null or empty values, Strings not containing a numerical representation or measurement unit, ambiguous values, etc. etc.

Store your values as Double as shown below
List<Double> where = new ArrayList<Double>();
where.add(0.5);
where.add(1);
where.add(5);
You can apply sorting on above list,
also at the time of retrieving you can use 0.5 kg instead of 500 grams if your app requirement allows it.

Mr #11thdimension has already given a perfect working solution. I think you can also use Java 8, if you want. Basically the same idea but less code.
List<String> where = new ArrayList<String>();
where.add("1 Kg");
where.add("500 gram");
where.add("5 Kg");
Collections.sort(where, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
double a = (Double.parseDouble(o1.replace("gram","").replace("Kg","").trim()))*(o1.contains("gram")?0.001:1);
double b = (Double.parseDouble(o2.replace("gram","").replace("Kg","").trim()))*(o2.contains("gram")?0.001:1);
return Double.compare(a,b) ;
}
});
where.stream().forEach(System.out::println);

just use index and for cycle,
for(int i=0; i<where.size(); i++){
System.out.println(where.get(i));
}

Related

Given a Map with values of type string, which are all numbers separated by commas, would it be possible to transform each value into a double?

I recently asked about converting Json using Gson into something I can sort values into, and the best option was using a Linked HashMap.
List<String> stringList = Arrays.asList(tm.split(" |,")); // split into pair key : value
Map<String, List<String>> mapString = new LinkedHashMap<>();
stringList.forEach(s1 -> {
String[] splitedStrings = s1.split(": "); //split into key : value
String key = splitedStrings[0].replaceAll("[^A-Za-z0-9]",""); // remove non alphanumeric from key, like {
String value = splitedStrings[1];
if (mapString.get(key) == null) {
List<String> values = new ArrayList<>();
values.add(value);
mapString.put(key, values);
}else if (mapString.get(key) != null) {
mapString.get(key).add(value);
}
});
When this code is run, a map with keys for frequency, magnitude, and other attributes of my data is created. This is the original Json Message compared to the resulting map value for the same set of data (Formatted to make it easier to understand and look better)
{"groupId":"id3_x_","timestamp":1.591712740507E9,"tones":
[{"frequency":1.074,"level":3.455,"bw":0.34,"snr":3.94,"median":0.877},
{"frequency":14.453,"level":2.656,"bw":0.391,"snr":2.324,"median":1.143},
{"frequency":24.902,"level":0.269,"bw":0.282,"snr":2.216,"median":0.121},
{"frequency":22.607,"level":0.375,"bw":0.424,"snr":2.034,"median":0.184},
{"frequency":9.863,"level":2.642,"bw":0.423,"snr":1.92,"median":1.376}]}
To Map values:
Message Received
Group ID: id3_x_
Message Topic: pi7/digest/tonals
Time of Arrival: 1.591712740507E9
---------------DATA---------------
Frequency: [1.07, 14.45, 24.90, 22.61, 9.86]
Magnitude: [3.46, 2.66, 0.27, 0.38, 2.64]
Bandwidth: [0.34, 0.39, 0.28, 0.42, 0.42]
SNR: [3.94, 2.32, 2.22, 2.03, 1.92]
Median: [0.88, 1.14, 0.12, 0.18, 1.38]]
While this is very useful for analyzing the data, the information stored is a string. What I would like to be able to do is transform each of the values in the map (Example: Frequency 1.07, 14.45, etc.) into doubles that i can then run through additional programs and run calculations with, such as an average. I have looked around online and havnt found anything that I am looking for, so im wondering if there would be a way to transform these strings into doubles using either an array, list, or any other means.
I am an intern for a tech company so I am still trying to hammer in Java and describing what I am talking about, so if there is any questions about what I am asking, please let me know and thanks in advance!
You could get a Map from the JSON file , you can also extract the values array from the Map yourmap.getvalues() , then you can parse each on of these element and case it into double
Example : Frequency: [1.07, 14.45, 24.90, 22.61, 9.86]
for ( String f : Frequency ) {
double f_double = Double.parse(f); // turns String into double
}
You can do this with another class that will store duplicate attribute values in arrays. You can simply get them through a.getValues (). This is just a concept and you should extend it as it will be convenient for you.
import java.util.*;
public class Main {
public static void main(String[] args) {
Map<String, List<Attribute>> map = new LinkedHashMap<>();
List<Attribute> attributes = new ArrayList<>();
attributes.add(new Attribute("frequency", 3.46, 5.11, 6.12));
attributes.add(new Attribute("magnitude", 3.46, 10.22, 10.54));
//and so on
map.put("idString1", attributes);
//printing double values
for (String key : map.keySet()) {
for (Attribute a : map.get(key)) {
System.out.println(a.getName() + " " +Arrays.toString(a.getValues()));
//a.getValues() to get all of doubles
}
}
}
private static class Attribute {
private String name;
private double[] values;
Attribute(String name, double... values) {
this.name = name;
this.values = values;
}
String getName() {
return name;
}
double[] getValues() {
return values;
}
}
}
The result will be:
frequency [3.46, 5.11, 6.12]
magnitude [3.46, 10.22, 10.54]
Your question:
I would like to be able to do is transform each of the String values in the
map (Example: Frequency 1.07, 14.45, etc.) into doubles and run calculations with, such as an average.
Yes, it is possible to transform your String array in a double array using Stream like below:
String[] frequencies = { "1.07", "14.45", "24.90", "22.61", "9.86" };
double[] arr = Stream.of(frequencies)
.mapToDouble(Double::parseDouble)
.toArray();
If you use the DoubleSummaryStatistics class you have already available ops like avg, sum :
String[] frequencies = { "1.07", "14.45", "24.90", "22.61", "9.86" };
DoubleSummaryStatistics statistics = Stream.of(frequencies)
.mapToDouble(Double::parseDouble)
.summaryStatistics();
System.out.println(statistics.getAverage()); //<-- 14.578
System.out.println(statistics.getMax()); //<-- 24.9

Hashmap: can get only last value

I'm putting some arrays into hashmap with 2 keys. Then I'm trying to extract these values, but can get values only for last keys. For any another keys I'm getting null pointer exception:
"Exception in thread "main" java.lang.RuntimeException: No xyValues
for the keys: 'L', '18.2' at
tmp.DataScan1.getSerie(DataScan1.java:49) at
tmp.DataScan1.main(DataScan1.java:66)"
What's wrong?
Here is my working example:
import java.util.Arrays;
import java.util.HashMap;
public class DataScan1 extends HashMap<Character, DataSerie>{
public static double[] freqs;
public void putSerie(char lriv, double freq, double[][] xyValues){
char key1 = lriv;
long key2 = double2key(round(freq, 4));
DataSerie dataSerie = new DataSerie();
dataSerie.put(key2, xyValues);
this.put(key1, dataSerie);
}
private static long double2key(double value){
long result = (long) (value * 10000);
return result;
}
public DataScan1(){
freqs = new double[]{1, 16.9,4.0,18.2,17.4};
for (int idxfreq=0; idxfreq<freqs.length; idxfreq++){
double[][] array = new double[][]{{1, 2}, {3,4}};
putSerie('L', freqs[idxfreq], array);
}
}
public double[][] getSerie(char lriv, double freq){
char key1 = lriv;
long key2 = double2key(round(freq, 4));
double[][] xyValues = this.get(key1).get(key2);
if (xyValues == null){
throw new RuntimeException("No xyValues for the keys: '" + lriv + "', '" + freq + "'");
}
return xyValues;
}
public static void printArr(double[] arr){
System.out.println(Arrays.toString(arr));
}
public static double round(double d, int decimalPlace) {
BigDecimal bd = new BigDecimal(Double.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd.doubleValue();
}
public static void main(String[] args) {
DataScan1 myData = new DataScan1();
printArr(myData.freqs);
double[][] qq = myData.getSerie('L', 17.4); // that serie exist
double[][] qqq = myData.getSerie('L', 18.2); // but this -- isn't, error here
}
}
UPDATE:
I forgot DataSerie definition:
public class DataSerie extends HashMap<Long, double[][]>{
}
Simply spoken: bad idea.
You want to use a floating point value as key for a Map. That means that Double objects will be created; and compared using their equals() methods.
And the thing is: you don't do that. When you compare two floating point numbers, you always always always do something along the lines of (x1 - x2) < epsilon. (see here for more examples why that is a bad idea; and then here for some alternatives; and finally here for some explanations; esp. answer no. 2 by Bernd)
In other words: if you really want to use those numbers as key, then keep them represented as strings!
And beyond that: avoid mixing concepts - you decided to use Maps; so don't make things more complicated by using arrays, too. I would rather define a "Matrix" class to hold the content that is currently in your double[][] array.
Finally, for your real problem with the current code:
this.put(key1, dataSerie);
Here you putting the new dataSerie object into your map. If you look closely, you will find that you keep using the same key (the char 'L') all the time. In other words: yes, your code creates a new dataSerie object; but then you are overwriting the one and only entry in your map with that value.
Thus, there are two ways to go:
In case that this map key is always the same character; well, then you do not need to use a map here. Then you would be using a List and just add your values!
If your program actually has to deal with different keys, then you might better use a Map<Char, List<double[][]>> for example.
But honestly; I think the real take away here is: step back, and work your way through this in order to really understand the things you intend to use!
In putSerie() you always create a new DataSerie and replace the old one at key 'L'
Instead, you should only create a new DataSerie there if none exists for key 'L'. If it does already exist, just take the existing one and insert your new element (xyValues) into it.
public void putSerie(char lriv, double freq, double[][] xyValues){
char key1 = lriv;
long key2 = double2key(freq);
DataSerie dataSerie;
if (!this.containsKey(key1)) {
// Only create a new one, when necessary
dataSerie = new DataSerie();
this.put(key1, dataSerie);
} else {
dataSerie = this.get(key1);
}
dataSerie.put(key2, xyValues);
}

Sorting a String ArrayList with numbers Java

Hello everyone I have a code using an arraylist, these are the test inputs to be added. But after I use the sort method. The output is not the one I expected.
ArrayList<String> test= new ArrayList<>();
test.add("2,2,17");
test.add("5,4,24 ");
test.add("8,1,11");
test.add("19,0,0");
test.add("2,3,21");
test.sort(null);
Output :
19,0,0
2,2,17
2,3,21
5,4,24
8,1,11
My desired out put should be :
2,2,17
2,3,21
5,4,24
8,1,11
19,0,0
Is there a way to sort "19,0,0" to be at the end, or any number to be add to make it the end of the arrayList?
You'll want to use Collections.sort, something like this:
Collections.sort(list, new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
int s1int = Integer.parseInt(s1.substring(0, s1.indexOf(",")));
int s2int = Integer.parseInt(s2.substring(0, s2.indexOf(",")));
return s1int - s2int;
}
});
You can use Collections.sort()
https://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#sort(java.util.List,%20java.util.Comparator).
And define a Comparator to compare the objects in the list.
In order for the strings to be sorted alphabetically rather than numerically, you will need to implement a comparator that converts the strings to integers for the comparison. You can then use this comparator with Collections.sort() to sort your list.
A better option would be to store your integers as integers in a 2D array rather than as strings (or some kind of nested list if the dimensions are not known up-front); however, I'd need to know more about how the data is created and used before uniformly proclaiming this to e the solution.
A possible flexible solution for Strings of various 'lengths', i.e. a different number of integers separated by commas.
Example list
2,2,17
5,4,24
19,0,2
8,1,11
19,0,1,2
19,0,1,4
2,3,21
2
Result after sorting
2
2,2,17
2,3,21
5,4,24
8,1,11
19,0,1,2
19,0,1,4
19,0,2
Code
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class SortExample {
public static void main(String[] args) {
List<String> test = new ArrayList<>();
test.add("2,2,17");
test.add("5,4,24");
test.add("19,0,2");
test.add("8,1,11");
test.add("19,0,1,2");
test.add("19,0,1,4");
test.add("2,3,21");
test.add("2");
Collections.sort(test, new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
String[] split1 = s1.split(",");
String[] split2 = s2.split(",");
final int indicesToCheck = Math.min(split1.length, split2.length);
int result = 0;
int i = 0;
while (result == 0 && i < indicesToCheck) {
result = Integer.compare(Integer.parseInt(split1[i]),
Integer.parseInt(split2[i]));
i++;
}
return result == 0 ? Integer.compare(split1.length, split2.length) : result;
}
});
for (String s : test) {
System.out.println(s);
}
}
}

Sorting an array of Strings with a numeric value, java

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

Sort components of a vector numerically which contain letter numbers and letters

Is there any way to sort a vector numerically?(i wanna sort the number before the first ; (semicolon)
I need let's say this (it's a vector with 4 String/components)
[
7394;dasd;dasda;dasda;5;3
2222;dasdasd;das;true;7;4;dsda;60
6660;dsada;dasasd;true;6;3
2345;dasdsagfd;das;true;7;4;gfgfdgd;60
]
to become this
[
2222;dasdasd;das;true;7;4;dsda;60
2345;dasdsagfd;das;true;7;4;gfgfdgd;60
6660;dsada;dasasd;true;6;3
7394;dasd;dasda;dasda;5;3
]
or this [3123;dasdas;31;31 1115;das;31;312 4412;sdf;31;42]
to [1115;das;31;312 3123;dasdas;31;31 4412;sdf;31;42]
(im sorting 3123, 1115, and 4412 numerically but i still keep the things after)
I've thought of converting each components to a string and then doing something like:
int count;
for(int i=0;i<string_component1.length();i++){
if(Character.isDigit(string_component1.charAt(i)){
count = i;
break;
}
}
and then with substring i would take the part i want, put it on a string, convert it to an int, then i would take the first one(lowest), use vector contains to find in which components its in and take this components to put it on a new vector at the first position. and so on with the others components
but i think its too much code for nothing and it wouldn't work since the vector size can be 3 or 50.
Is there any way to sort a vector numerically?(i want to sort the number before the first ; (semicolon) )
The general pattern to sort a java.util.Vector is to implement a Comparator and pass it to the Collections.sort() method. All the logic for sorting order can then be put into the compare() method of the Comparator. In your case, it could look like this:
public static void main(String[] args) {
String[] input = {"7394;dasd;dasda;dasda;5;3", "2222;dasdasd;das;true;7;4;dsda;60",
"6660;dsada;dasasd;true;6;3", "2345;dasdsagfd;das;true;7;4;gfgfdgd;60"};
Vector<String> vec = new Vector<>();
vec.addAll(Arrays.asList(input));
System.out.println("Input : " + vec);
Collections.sort(vec, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
int i1 = Integer.valueOf(o1.split(";")[0]);
int i2 = Integer.valueOf(o2.split(";")[0]);
return i1 - i2;
}
} );
System.out.println("Result: " + vec);
}
Output:
Input : [7394;dasd;dasda;dasda;5;3, 2222;dasdasd;das;true;7;4;dsda;60, 6660;dsada;dasasd;true;6;3, 2345;dasdsagfd;das;true;7;4;gfgfdgd;60]
Result: [2222;dasdasd;das;true;7;4;dsda;60, 2345;dasdsagfd;das;true;7;4;gfgfdgd;60, 6660;dsada;dasasd;true;6;3, 7394;dasd;dasda;dasda;5;3]
Here's the elegant way of doing it using Collections.sort() with a Comparator:
Vector<String> vector;
Collections.sort(vector, new Comparator<String>() {
public int compare(String s1, String s2) {
return getInt(s1) - getInt(s2);
}
int getInt(String s) {
return Integer.parseInt(s.replaceAll("(\\d+).*", "$1"));
}
});
I couldn't understand your sorting order, but you can sort a collection using a Collection.sort(),
You can use Collection.sort() to sort your list and implement a custom Comparator to order elements the way you want.
If you strings have a fix pattern you could do something like:
int indexOfSemi = string_component.indexOf(";")
With this you have the first semicolon.
And then with:
String myNumber = string_component.substring(0,indexOfSemi-1)
And now you have your number which you must convert to int.
Do this in a Comparator class and you have your comparing mechanism.

Categories