Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sorting 2D array of strings in Java

I know that this question might have been asked before, but I was not able to find a fit answer. So say I have this array:

String[][] theArray = {
        {"james", "30.0"},
        {"joyce", "35.0"},
        {"frank", "3.0"},
        {"zach", "34.0"}};

Is there a way to descendingly sort this array by the second element of each sub-element. So I would get something like this.

theArray = {
        {"joyce", "35.0"},
        {"zach", "34.0"},
        {"james", "30.0"},
        {"frank", "3.0"}};
like image 650
Julio Diaz Avatar asked Feb 21 '11 09:02

Julio Diaz


4 Answers

Use Arrays.sort(arr, comparator) with a custom comparator:

Arrays.sort(theArray, new Comparator<String[]>(){

    @Override
    public int compare(final String[] first, final String[] second){
        // here you should usually check that first and second
        // a) are not null and b) have at least two items
        // updated after comments: comparing Double, not Strings
        // makes more sense, thanks Bart Kiers
        return Double.valueOf(second[1]).compareTo(
            Double.valueOf(first[1])
        );
    }
});
System.out.println(Arrays.deepToString(theArray));

Output:

[[joyce, 35.0], [zach, 34.0], [james, 30.0], [frank, 23.0]]


Beware:

you will be sorting the array you passed in, Arrays.sort() will not return a new array (in fact it returns void). If you want a sorted copy, do this:

String[][] theCopy = Arrays.copyOf(theArray, theArray.length);

And perform the sorting on theCopy, not theArray.

like image 68
Sean Patrick Floyd Avatar answered Nov 10 '22 17:11

Sean Patrick Floyd


You must use the Arrays.sort() method. This method takes a Comparator as argument. The sort method delegates to the comparator to determine if one element of the array must be considered bigger, smaller or equal to another element. Since every element of the outer array is an array, the comparator will have to compare arrays (of Strings).

The arrays must be compared based on the value of their second element. This second element is a String which in fact represents a double number. So you'll have to transorm the strings into numbers, else the order will be lexicographical (20 come before 3) rather than numerical.

The comparator could thus look like this :

public class StrinArrayComparator implements Comparator<String[]> {
    @Override
    public int compare(String[] array1, String[] array2) {
        // get the second element of each array, andtransform it into a Double
        Double d1 = Double.valueOf(array1.[1]);
        Double d2 = Double.valueOf(array2.[1]);
        // since you want a descending order, you need to negate the 
        // comparison of the double
        return -d1.compareTo(d2);
        // or : return d2.compareTo(d1);
    }
}
like image 32
JB Nizet Avatar answered Nov 10 '22 19:11

JB Nizet


If you want to move away from arrays, here's a variation that uses List<Record> and a RecordComparator that implements Comparator<Record>.

Console:

joyce 35.0
zach 34.0
james 30.0
frank 23.0

Code:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/** @see http://stackoverflow.com/questions/5064027 */
public class ComparatorTest {
    public static void main(String[] args) {
        List<Record> list = new ArrayList<Record>(Arrays.asList(
            new Record("james", "30.0"),
            new Record("joyce", "35.0"),
            new Record("frank", "23.0"),
            new Record("zach",  "34.0")));
        print(list, Sort.DESCENDING, Field.D);
    }

    private static void print(List<Record> list, Sort s, Field f) {
        RecordComparator rc = new RecordComparator(s, f);
        Collections.sort(list, rc);
        for (Record r : list) {
            System.out.println(r);
        }
    }
}

class Record {

    private String s;
    private Double d;

    public Record(String name, String number) {
        this.s = name;
        this.d = Double.valueOf(number);
    }

    @Override
    public String toString() {
        return s + " " + d;
    }

    public int compareTo(Field field, Record record) {
        switch (field) {
            case S: return this.s.compareTo(record.s);
            case D: return this.d.compareTo(record.d);
            default: throw new IllegalArgumentException(
                "Unable to sort Records by " + field.getType());
        }
    }
}

enum Sort { ASCENDING, DESCENDING; }

enum Field {

    S(String.class), D(Double.class);

    private Class type;

    Field(Class<? extends Comparable> type) {
        this.type = type;
    }

    public Class getType() {
        return type;
    }
}

class RecordComparator implements Comparator<Record> {

    private Field field;
    private Sort sort;

    public RecordComparator(Sort sort, Field field) {
        this.sort = sort;
        this.field = field;
    }

    @Override
    public final int compare(Record a, Record b) {
        int result = a.compareTo(field, b);
        if (sort == Sort.ASCENDING) return result;
        else return -result;
    }
}
like image 3
trashgod Avatar answered Nov 10 '22 17:11

trashgod


You seem to be living in object denial. Those inner arrays look a lot like information about a Person (with the name and some value, maybe a score).

What you'd want to do is to write a custom class to hold that information:

public class Person {
  private final String name;
  private final double score;

  public Person(final String name, final double score) {
    this.name=name;
    this.score=score;
  }

  public String getName() {
    return name;
  }

  public double getScore() {
    return score;
  }
}

Then, when you want to sort them, you simply implement a Comparator<Person> that specifies how you want them sorted:

public PersonScoreComparator implements Comparator<Person> {
  public int compare(Person p1, Person p2) {
    return Double.compare(p1.getScore(), p2.getScore());
  }
}

Alternatively, you could have the Person class itself implement Comparable<Person> by adding this method:

public int compareTo(Person other) {
  return Double.compare(getScore(), other.getScore());
}
like image 2
Joachim Sauer Avatar answered Nov 10 '22 19:11

Joachim Sauer