How do I sort two arrays in relation to each other?
Solution 1
Here's complete code:
StringIntTuple.java:
public class StringIntTuple{
public final int intValue;
public final String stringValue;
public StringIntTuple(int intValue, String stringValue){
this.intValue = intValue;
this.stringValue = stringValue;
}
public String toString(){
return "(" + this.intValue + ", " + this.stringValue + ")";
}
}
StringIntTupleStringComparator.java:
import java.util.Comparator;
public class StringIntTupleStringComparator implements
Comparator<StringIntTuple> {
@Override
public int compare(StringIntTuple a, StringIntTuple b) {
// TODO Auto-generated method stub
return a.stringValue.compareTo(b.stringValue);
}
}
StringIntTupleIntComparator.java:
import java.util.Comparator;
public class StringIntTupleIntComparator implements Comparator<StringIntTuple> {
@Override
public int compare(StringIntTuple a,
StringIntTuple b) {
return ((Integer)a.intValue).compareTo((Integer)b.intValue);
}
}
Driver.java:
import java.util.ArrayList;
import java.util.Collections;
public class Driver {
/**
* @param args
*/
public static String[] names = new String[] {"Monkey1", "Dog2", "Horse3", "Cow4", "Spider5"};
public static int[] data = new int[] {1,2,3,4,5};
public static void main(String[] args) {
ArrayList<StringIntTuple> list = new ArrayList<StringIntTuple>();
for(int i =0; i<names.length; i++){
list.add(new StringIntTuple(data[i],names[i]));
}
Collections.sort(list, new StringIntTupleIntComparator());
System.out.println(list.toString());
Collections.sort(list, new StringIntTupleStringComparator());
System.out.println(list.toString());
}
}
Output (sorted first by int field, then by String field):
[(1, Monkey1), (2, Dog2), (3, Horse3), (4, Cow4), (5, Spider5)]
[(4, Cow4), (2, Dog2), (3, Horse3), (1, Monkey1), (5, Spider5)]
EDIT 1 (extra info):
If you want to make this work for any Tuple, i.e. which doesn't constrain the field types to int, String, you can simply do the same operation with generics, i.e.:
public class Tuple<A,B>{
public Tuple(A aValue, B bValue){
this.aValue = aValue;
this.bValue = bValue;
}
public final A aValue;
public final B bValue;
}
Then, just tweak the Comparators accordingly, and you have a generic solution. EDIT 2(After lunch): Here it is.
public class TupleAComparator<A extends Comparable<A>,B extends Comparable<B>> implements Comparator<Tuple<A,B>> {
@Override
public int compare(Tuple<A, B> t1, Tuple<A, B> t2) {
return t1.aValue.compareTo(t2.aValue);
}
}
EDIT 3: Code supplement as answer to Comment #1 (augmenting comment #2) TupleArrayList.java:
import java.util.ArrayList;
import java.util.List;
public class TupleArrayList<A,B> extends ArrayList<Tuple<A,B>> {
/**
* An ArrayList for tuples that can generate a List of tuples' elements from a specific position within each tuple
*/
private static final long serialVersionUID = -6931669375802967253L;
public List<A> GetAValues(){
ArrayList<A> aArr = new ArrayList<A>(this.size());
for(Tuple<A,B> tuple : this){
aArr.add(tuple.aValue);
}
return aArr;
}
public List<B> GetBValues(){
ArrayList<B> bArr = new ArrayList<B>(this.size());
for(Tuple<A,B> tuple : this){
bArr.add(tuple.bValue);
}
return bArr;
}
}
Solution 2
If you really don't want to redo your data-structures to combine the infos, you can use a Multimap to do it.
This example utilizes the excellent Google-Guava Library, which you should be using anyway :) https://code.google.com/p/guava-libraries/
String[] names = new String[] {"Monkey1", "Dog2", "Horse3", "Cow4", "Spider5"};
int[] data = new int[] {1,2,3,4,5};
/* guava, throws an IllegalStateException if your array aren't of the same length */
Preconditions.checkState(names.length == data.length, "data and names must be of equal length");
/* put your values in a MultiMap */
Multimap<String, Integer> multiMap = LinkedListMultimap.create();
for (int i=0; i<names.length; i++) {
mmap.put(names[i], data[i]);
}
/* our output, 'newArrayList()' is just a guava convenience function */
List<String> sortedNames = Lists.newArrayList();
List<Integer> sortedData = Lists.newArrayList();
/* cycle through a sorted copy of the MultiMap's keys... */
for (String name : Ordering.natural().sortedCopy(mmap.keys())) {
/* ...and add all of the associated values to the lists */
for (Integer value : mmap.get(name)) {
sortedNames.add(name);
sortedData.add(value);
}
}
Solution 3
So the obvious answer here is to wrap the name
and data
values in a class. Then maintain a List of that class. The class should implement equals
, hashCode
and Comparable
which then then allow sorting the list using Collections.sort
.
Maintain related data in two different lists is anti-OOP.
Something like this.
class MyWrapper implements Comparable<MyWrapper>{
private String name;
private int data;
}
List<MyWrapper> listToBeSorted;
Solution 4
In some cases it doesn't make much sense to create a new class just to do concurrent sorting.
Here, is a function that can be used to sort an arbitrary number of List
s with arbitrary types based on a key that implement Comparable
(Ideone Example here).
Usage
Here is an example of how you can use the function to sort multiple lists of arbitrary types:
// Can be any type that implements Comparable, Dupes are allowed
List<Integer> key = Arrays.asList(4, 3, 1, 2, 1);
// List Types do not need to be the same
List<String> list1 = Arrays.asList("Four", "Three", "One", "Two", "One");
List<Character> list2 = Arrays.asList('d', 'c', 'a', 'b', 'a');
// Sorts key, list1, list2
// Remove second key if you don't want to sort key.
multiSort(key, key, list1, list2);
Output:
key: [1, 1, 2, 3, 4]
list1: [One, One, Two, Three, Four]
list2: [a, a, b, c, d]
Code
An Ideone Example can be found here which includes validation of parameters and a test case.
public static <T extends Comparable<T>> void multiSort(
final List<T> key, List<?>... lists){
// Create a List of indices
List<Integer> indices = new ArrayList<Integer>();
for(int i = 0; i < key.size(); i++) {
indices.add(i);
}
// Sort the indices list based on the key
Collections.sort(indices, new Comparator<Integer>() {
@Override public int compare(Integer i, Integer j) {
return key.get(i).compareTo(key.get(j));
}
});
// Create a mapping that allows sorting of the List by N swaps.
// Only swaps can be used since we do not know the type of the lists
Map<Integer,Integer> swapMap = new HashMap<Integer, Integer>(indices.size());
List<Integer> swapFrom = new ArrayList<Integer>(indices.size()),
swapTo = new ArrayList<Integer>(indices.size());
for (int i = 0; i < key.size(); i++) {
int k = indices.get(i);
while (i != k && swapMap.containsKey(k)) {
k = swapMap.get(k);
}
swapFrom.add(i);
swapTo.add(k);
swapMap.put(i, k);
}
// use the swap order to sort each list by swapping elements
for (List<?> list : lists)
for (int i = 0; i < list.size(); i++)
Collections.swap(list, swapFrom.get(i), swapTo.get(i));
}
Solution 5
The "right" way to do this in Java is to create a combined object that holds the corresponding elements, and to sort that.
Example:
class NameAndData {
private final String name;
private final int data;
}
List<NameAndData> toBeSorted;
and then you create a list of the combined elements and sort that. Basically, you're writing your own specific Pair
class. (I, and many Java developers, think that adding a Pair
class to Java would just lead to more obfuscated code -- a LatLong
class, for example, is much less ambiguous about what it means than a Pair<Double, Double>
.)
Comments
-
Lostsoul over 1 year
I prototype in python and I'm used the zip function for this, I'm not sure how to do this in Java. Basically I have two lists (one is
names
and one isdata
) and want them sorted in relation to each other. My program only processes a list (data
, in this case) but I use thenames
as a reference to whatdata
I'm processing and I want to try to experiment with processing my data in a different order. Here's an example of the structure (in reality my data is not given to me stored but I would do either a basic sort or a reverse sort on it, nothing fancy).String[] names = new String[]{"Monkey1", "Dog2", "Horse3", "Cow4", "Spider5"}; int[] data = new int[]{1, 2, 3, 4, 5};
so the inverse would be
name = Spider5, Cow4, Horse3, Dog2, Monkey1 data = 5, 4, 3, 2, 1
I found this question: Is there an accepted Java equivalent to Python's zip(), but I would rather (if possible and for the faint of heart) do this using libraries I already have (Java commons, apache commons, etc). If there's no other way then I'll give
functional java
a shot. Any suggestions?