How to sort List<String> list numerically? - java

I have a List<String> list which is initialized to an arrayList. That is,
List<String>list = new ArrayList();
It has the following elements.
[1,bread, 1,turkey, 10,potato, 11,plum, 12,carrot, 2,milk, 2,rice]
I would like to sort the list so that the numbers are in ascending order. For example,
[1,bread,1 turkey,2,milk,2,rice,10,potato,11,plum,12,carrot]
How can I do that?

Java is an Object-Oriented language, and you should use it.
So, create a new class with two fields: int and String.
Now parse your strings and create objects, i.e. 1,bread is parsed into the int value 1, and the String value bread.
Next, make your class implement Comparable, and implement the compareTo method to order the objects by the int value.
Finally, now that List<String> was converted to List<MyObj>, call Collections.sort(list).

You're not trying to sort the elements in the List--you're trying to sort pairs of elements. You can't do that with a simple sort. What you'll need to do is:
Define a class with two fields, an int and a String. Make the class implement Comparable.
Define a comparator for the class that compares the int fields to get the order you want. You'll have to decide what your comparator will do if the int fields are equal (do you want the String fields to be in ascending order?)
Create a List<YourClass> whose size is half the size of the original list, by going through the source list in pairs, something like
for (int i = 0; i < list.size() - 1; i += 2) {
create a YourClass by converting list.get(i) to an int, and using list.get(i+1) as the String field
}
Sort the new list
If desired, recreate a List<String> by going through the List<YourClass> and adding a String conversion of the int, followed by the String field from YourClass, to the new list.
I don't know what you're planning to do with the String list, but in most cases it will make your program easier if you create a List<YourClass> list as soon as possible, and work with YourClass objects throughout the rest of the program

The simple answer is that you could provide a custom Comparator which understands the structure of each individual String element and can parse and compare them properly. Something like this:
#Test
public void testShouldSortByNumber() {
// Arrange
List<String> list = Arrays.asList("1,bread", "1,turkey", "10,potato", "11,plum", "12,carrot", "2,milk", "2,rice");
final List<String> EXPECTED_LIST = Arrays.asList("1,bread", "1,turkey", "2,milk", "2,rice", "10,potato", "11,plum", "12,carrot");
// Act
Collections.sort(list, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
try {
int i1 = Integer.parseInt(o1.split(",")[0]);
int i2 = Integer.parseInt(o2.split(",")[0]);
// If the numbers are equal, could order by alpha on the second part of the string split
return i1 < i2 ? -1 : i1 == i2 ? 0 : 1;
} catch (Exception e) {
// Lots of possible errors above -- NPE, NFE, invalid string format, etc.
e.printStackTrace();
}
return 0;
}
});
// Assert
assert list.equals(EXPECTED_LIST);
}
The more complex answer is that you should better define your problem -- what should the result be if an element is empty or null, if the numbers are equal are the other strings compared lexicographically or is it irrelevant?
You may also want to use a different data structure -- if the content of each element is really two different logical concepts, a tuple or class may be correct. Or, you may want to use a SortedMap (of which TreeMap is probably the best implementation here) where the key is the "ingredient" and the value is the "count" (or "cost", I don't have any context on the numerical value).
You can also enhance the code above with a lambda if you have access to JDK 8+.

Related

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.

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."

How do filter out this list with java 8 streams and functional interfaces?

if I have a list of arrays like this (pseudo java code):
Note the list valsSorted will be always sorted with x[0] asc and x[1] desc order.
List valsSorted = {[1 5][1 4][1 3][2 1][3 2][3 1][4 2][4 1][5 1][6 2][6 1]};
How do I filter this list with Java 8 streams and lambdas so that I get:
result = {[1 5][2 1][3 2][4 2][5 1][6 2]}
The first item of the array (x[0]) is ID and the second is a version number. So the rule is give all distinct IDs with the highest version back.
If I would use a for loop the following code would be fine:
ArrayList<int[]> result= new ArrayList();
int keep = -1;
for (int[] x : valsSorted) {
int id = x[0];
int version = x[1];
if(keep == id) continue;
keep = id;
result.add(x);
}
Your use of the word "distinct" suggests using the distinct() stream operation. Unfortunately that operation is hardwired to use the equals() method of the stream elements, which isn't useful for arrays. One approach for dealing with this would be to wrap the arrays in a wrapper object that has the semantics of equality that you're looking for:
class Wrapper {
final int[] array;
Wrapper(int[] array) { this.array = array; }
int[] getArray() { return array; }
#Override
public boolean equals(Object other) {
if (! (other instanceof Wrapper))
return false;
else
return this.array[0] == ((Wrapper)other).array[0];
}
#Override
public int hashCode() { ... }
}
Then wrap up your object before distinct() and unwrap it after:
List<int[]> valsDistinct =
valsSorted.stream()
.map(Wrapper::new)
.distinct()
.map(Wrapper::getArray)
.collect(toList());
This makes one pass over the data but it generates a garbage object per value. This also relies on the stream elements being processed in-order since you want the first one.
Another approach would be to use some kind of stateful collector, but that will end up storing the entire result list before any subsequent processing begins, which you said you wanted to avoid.
It might be worth considering making the data elements be actual classes instead of two-element arrays. This way you can provide a reasonable notion of equality, and you can also make the values comparable so that you can sort them easily.
(Credit: technique stolen from this answer.)
class Test{
List<Point> valsSorted = Arrays.asList(new Point(1,5),
new Point(1,4),
new Point(1,3),
new Point(2,1),
new Point(3,2),
new Point(3,1),
new Point(4,2),
new Point(4,1),
new Point(5,1),
new Point(6,2),
new Point(6,1));
public Test(){
List<Point> c = valsSorted.stream()
.collect(Collectors.groupingBy(Point::getX))
.values()
.stream()
.map(j -> j.get(0))
.collect(Collectors.toList());
for(int i=0; i < c.size(); i++){
System.out.println(c.get(i));
}
}
public static void main(String []args){
Test t = new Test()
}
}
I decided to use the point class and represent the ID field as x and the version number as Y. So from there if you create a stream and group them by ID. You can call the values method which returns a Collection of Lists Collection<List<Point>>. You can then call the stream for this Collection and get the first value from each list which according to your specifications is ordered with descending version number so it should be the the highest version number. From there all you have to do is collect them into a list, array or whatever you see necessary and assign it as needed.
The only problem here is that they are printed out of order. That should be an easy fix though.

How to convert a List of String arrays to a List of objects

I want to convert a List to a List so that each object on my new list includes the first element of each String[].
Do you know if this is possible to do in java?
for example:
public List<String[]> readFile(){
String[]array1={"A","1.3","2.4","2.3"};
String[]array2={"B","1.43","3.4","2.8"};
String[]array3={"C","5.23","2.45","2.9"};
List<String[]>ReadFile= new ArrayList<String[]>();
ReadFile.add(array1);
ReadFile.add(array2);
ReadFile.add(array3);
return ReadFile;
}
Now I want a method which will take the List ReadFile from above to somehow split the arrays of strings into an ID which will be the first element "A", "B", "C" and another part which would be the string array of numbers which I will put through another method to convert numbers from String to type Double. I have already got the method to convert to double but I need to be able to keep track of the ID field because the ID field will be used to identify the array of numbers.
A friend suggested that I create an Object where each objects has one part as a String ID and the other part as an array. That is the part which I do not know how to do.
Can anybody help please?
below is the method declaration which I believe I should have so the return type will be List where each array has been converted to an Object with two parts.
public List<Object> CreateObject(List<String[]>ReadFile){
}
Thanks,
Jetnori.
Hi all, Thank you for taking your time to help.
I can see the benefit of using HashTables. I am as of now trying to implement it. I know i might be sidetracking a little but just to explain what I am trying to do:
In my project I have CSV file with data about gene expression levels. The method that I use from OpenCSV to read the file returns a List(String[]) where each String[] is one row in the file. The first element of each row is variable name (recA, ybjE etc). The rest of the row will be numbers data related to that variable. I want to calculate Pearson's correlation between each of the number arrays. The method which I have got implemented already does that for me but the problem that I have now is that I had to remove the string values from my arrays before I could convert to double by iterating over the array. After I have managed to calculate the correlation between each array of doubles by still keeping the ID linked to the row, I want to be able to draw an undirected node graph between the genes that have a correlation higher than a threshold which I will set (for example correlation higher than 0.80). I don't know if i am biting more than i can chew but I have 30 days to do it and I believe that with the help of people like you guys I will get through it.
Sorry for going on for a bit.
thanks,
Jetnori.
I agree with the answer Alb provided, however this is what your friend has suggested, first you need a class to represent the data. I have included a constructor that parses the data and one that accepts already parsed data, depending on how you like to think of things.
public class NumberList {
private double[] numbers;
private String key;
public NumberList(Strig key, double[] numbers){
this.ley = key;
this.numbers = numbers;
}
public NumberList(String[] inputList) {
key = inputList[0];
numbers = new double[inputList.length-1];
for(int i=1;i<inputList.length;i++){
numers[i-1] = Double.parseDouble(inputList[i]);
}
}
public String getKey() {
return key;
}
public double[] getNumbers() {
return numbers;
}
}
Then you need your function to generate the list:
public List<NumberList> CreateObject(List<String[]> ReadFile){
ArrayList<NumberList> returnList = new ArrayList<NumberList>(ReadFile.size());
for (String[] input : ReadFile) {
returnList.add(new NumberList(input));
}
return returnList;
}
Note this uses the constructor that parses the data, if you use the other constructor then the "CreateObject" function would need to include the parsing logic.
Finally on a side note the standard convention in java is that the only thing that is capitalized are class names and final static fields (which appear in all caps sepearted by underscores), so conventionally the method signature would be:
public List<NumberList> createObject(List<String[]> readFile){
...
}
Sounds like you need a Map instead of a List, it lets you index things by a key (in your case ID).
Map<String, String[]> map = new Hashmap<String, String[]>();
for( String[] array : ReadFile ){
map.put( array[0], array );
}
then to get the array of values for 'A' you would do:
String[] values = map.get( "a" );
If you want the values to be doubles instead of strings you'll want to change the array before putting it (the map.put call) I'd advise using a list or other collections instead of an array also. You also will probably also want to remove the ID part from these values, which my code does not do.
public class Split_ListwithIDs {
Hashtable<String, String[]> table = new Hashtable<String, String[]>();
Splitter spl ;
public Split_ListwithIDs(Splitter split){
spl = split;
}
private void addEntry(String key , String[] vals){
table.put(key, vals);
}
public void parseList(List<String[]> list){
for(String[] entry : list){
String[] temp = new String[entry.length - 1];
System.arraycopy(entry, 1, temp, 0,entry.length - 1);
addEntry(entry[0], spl.GetStringArrayOfNumbers(temp));
}
}
class SplittingHelper implements Splitter{
#Override
public String[] GetStringArrayOfNumbers(String[] arr) {
String[] strArray = null ;
// implementation here
return arr;
}
}
interface Splitter {
String[] GetStringArrayOfNumbers(String[] arr);
}
}
You will have to use a Hashtable instead of a list of objects.( I am assuming that you will need to search through the list for a given entry using the First alphabet as key - This will be very laborious if you want to use a List ).
In the method SplittingHelper , provide your custom logic to parse the string of numbers and return another string[] of numbers.
I don't understand your goal, but for 'an object with 2 parts' you might consider storing them in a Hashtable: http://download.oracle.com/javase/6/docs/api/java/util/Hashtable.html

Compare new Integer Objects in ArrayList Question

I am storing Integer objects representing an index of objects I want to track. Later in my code I want to check to see if a particular object's index corresponds to one of those Integers I stored earlier. I am doing this by creating an ArrayList and creating a new Integer from the index of a for loop:
ArrayList<Integer> courseselectItems = new ArrayList();
//Find the course elements that are within a courseselect element and add their indicies to the ArrayList
for(int i=0; i<numberElementsInNodeList; i++) {
if (nodeList.item(i).getParentNode().getNodeName().equals("courseselect")) {
courseselectItems.add(new Integer(i));
}
}
I then want to check later if the ArrayList contains a particular index:
//Cycle through the namedNodeMap array to find each of the course codes
for(int i=0; i<numberElementsInNodeList; i++) {
if(!courseselectItems.contains(new Integer(i))) {
//Do Stuff
}
}
My question is, when I create a new Integer by using new Integer(i) will I be able to compare integers using ArrayList.contains()? That is to say, when I create a new object using new Integer(i), will that be the same as the previously created Integer object if the int value used to create them are the same?
I hope I didn't make this too unclear. Thanks for the help!
Yes, you can use List.contains() as that uses equals() and an Integer supports that when comparing to other Integers.
Also, because of auto-boxing you can simply write:
List<Integer> list = new ArrayList<Integer>();
...
if (list.contains(37)) { // auto-boxed to Integer
...
}
It's worth mentioning that:
List list = new ArrayList();
list.add(new Integer(37));
if (list.contains(new Long(37)) {
...
}
will always return false because an Integer is not a Long. This trips up most people at some point.
Lastly, try and make your variables that are Java Collections of the interface type not the concrete type so:
List<Integer> courseselectItems = new ArrayList();
not
ArrayList<Integer> courseselectItems = new ArrayList();
My question is, when I create a new Integer by using new Integer(i) will I be able to compare integers using ArrayList.contains()? That is to say, when I create a new object using new Integer(i), will that be the same as the previously created Integer object if the int value used to create them are the same?
The short answer is yes.
The long answer is ...
That is to say, when I create a new object using new Integer(i), will that be the same as the previously created Integer object if the int value used to create them are the same?
I assume you mean "... will that be the same instance as ..."? The answer to that is no - calling new will always create a distinct instance separate from the previous instance, even if the constructor parameters are identical.
However, despite having separate identity, these two objects will have equivalent value, i.e. calling .equals() between them will return true.
Collection.contains()
It turns out that having separate instances of equivalent value (.equals() returns true) is okay. The .contains() method is in the Collection interface. The Javadoc description for .contains() says:
http://java.sun.com/javase/6/docs/api/java/util/Collection.html#contains(java.lang.Object)
boolean contains(Object o)
Returns true if this collection
contains the specified element. More
formally, returns true if and only if
this collection contains at least one
element e such that (o==null ? e==null
: o.equals(e)).
Thus, it will do what you want.
Data Structure
You should also consider whether you have the right data structure.
Is the list solely about containment? is the order important? Do you care about duplicates? Since a list is order, using a list can imply that your code cares about ordering. Or that you need to maintain duplicates in the data structure.
However, if order is not important, if you don't want or won't have duplicates, and if you really only use this data structure to test whether contains a specific value, then you might want to consider whether you should be using a Set instead.
Short answer is yes, you should be able to do ArrayList.contains(new Integer(14)), for example, to see if 14 is in the list. The reason is that Integer overrides the equals method to compare itself correctly against other instances with the same value.
Yes it will, because List.contains() use the equals() method of the object to be compared. And Integer.equals() does compare the integer value.
As cletus and DJ mentioned, your approach will work.
I don't know the context of your code, but if you don't care about the particular indices, consider the following style also:
List<Node> courseSelectNodes = new ArrayList<Node>();
//Find the course elements that are within a courseselect element
//and add them to the ArrayList
for(Node node : numberElementsInNodeList) {
if (node.getParentNode().getNodeName().equals("courseselect")) {
courseSelectNodes.add(node);
}
}
// Do stuff with courseSelectNodes
for(Node node : courseSelectNodes) {
//Do Stuff
}
I'm putting my answer in the form of a (passing) test, as an example of how you might research this yourself. Not to discourage you from using SO - it's great - just to try to promote characterization tests.
import java.util.ArrayList;
import junit.framework.TestCase;
public class ContainsTest extends TestCase {
public void testContains() throws Exception {
ArrayList<Integer> list = new ArrayList<Integer>();
assertFalse(list.contains(new Integer(17)));
list.add(new Integer(17));
assertTrue(list.contains(new Integer(17)));
}
}
Yes, automatic boxing occurs but this results in a performance penalty. Its not clear from your example why you would want to solve the problem in this manner.
Also, because of boxing, creating the Integer class by hand is superfluous.

Categories

Resources