How to Sort Two Arrays in Relation to Each Other

How to sort two lists (which reference each other) in the exact same way

One classic approach to this problem is to use the "decorate, sort, undecorate" idiom, which is especially simple using python's built-in zip function:

>>> list1 = [3,2,4,1, 1]
>>> list2 = ['three', 'two', 'four', 'one', 'one2']
>>> list1, list2 = zip(*sorted(zip(list1, list2)))
>>> list1
(1, 1, 2, 3, 4)
>>> list2
('one', 'one2', 'two', 'three', 'four')

These of course are no longer lists, but that's easily remedied, if it matters:

>>> list1, list2 = (list(t) for t in zip(*sorted(zip(list1, list2))))
>>> list1
[1, 1, 2, 3, 4]
>>> list2
['one', 'one2', 'two', 'three', 'four']

It's worth noting that the above may sacrifice speed for terseness; the in-place version, which takes up 3 lines, is a tad faster on my machine for small lists:

>>> %timeit zip(*sorted(zip(list1, list2)))
100000 loops, best of 3: 3.3 us per loop
>>> %timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100000 loops, best of 3: 2.84 us per loop

On the other hand, for larger lists, the one-line version could be faster:

>>> %timeit zip(*sorted(zip(list1, list2)))
100 loops, best of 3: 8.09 ms per loop
>>> %timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100 loops, best of 3: 8.51 ms per loop

As Quantum7 points out, JSF's suggestion is a bit faster still, but it will probably only ever be a little bit faster, because Python uses the very same DSU idiom internally for all key-based sorts. It's just happening a little closer to the bare metal. (This shows just how well optimized the zip routines are!)

I think the zip-based approach is more flexible and is a little more readable, so I prefer it.


Note that when elements of list1 are equal, this approach will end up comparing elements of list2. If elements of list2 don't support comparison, or don't produce a boolean when compared (for example, if list2 is a list of NumPy arrays), this will fail, and if elements of list2 are very expensive to compare, it might be better to avoid comparison anyway.

In that case, you can sort indices as suggested in jfs's answer, or you can give the sort a key function that avoids comparing elements of list2:

result1, result2 = zip(*sorted(zip(list1, list2), key=lambda x: x[0]))

Also, the use of zip(*...) as a transpose fails when the input is empty. If your inputs might be empty, you will have to handle that case separately.

Sort two arrays the same way

You can sort the existing arrays, or reorganize the data.

Method 1:
To use the existing arrays, you can combine, sort, and separate them:
(Assuming equal length arrays)

var names = ["Bob","Tom","Larry"];
var ages = ["10", "20", "30"];

//1) combine the arrays:
var list = [];
for (var j = 0; j < names.length; j++)
list.push({'name': names[j], 'age': ages[j]});

//2) sort:
list.sort(function(a, b) {
return ((a.name < b.name) ? -1 : ((a.name == b.name) ? 0 : 1));
//Sort could be modified to, for example, sort on the age
// if the name is the same. See Bonus section below
});

//3) separate them back out:
for (var k = 0; k < list.length; k++) {
names[k] = list[k].name;
ages[k] = list[k].age;
}

This has the advantage of not relying on string parsing techniques, and could be used on any number of arrays that need to be sorted together.

Method 2: Or you can reorganize the data a bit, and just sort a collection of objects:

var list = [
{name: "Bob", age: 10},
{name: "Tom", age: 20},
{name: "Larry", age: 30}
];

list.sort(function(a, b) {
return ((a.name < b.name) ? -1 : ((a.name == b.name) ? 0 : 1));
});

for (var i = 0; i<list.length; i++) {
alert(list[i].name + ", " + list[i].age);
}

For the comparisons,-1 means lower index, 0 means equal, and 1 means higher index. And it is worth noting that sort() actually changes the underlying array.

Also worth noting, method 2 is more efficient as you do not have to loop through the entire list twice in addition to the sort.

http://jsfiddle.net/ghBn7/38/

Bonus Here is a generic sort method that takes one or more property names.

function sort_by_property(list, property_name_list) {
list.sort((a, b) => {
for (var p = 0; p < property_name_list.length; p++) {
prop = property_name_list[p];
if (a[prop] < b[prop]) {
return -1;
} else if (a[prop] !== a[prop]) {
return 1;
}
}
return 0;
});
}

Usage:

var list = [
{name: "Bob", age: 10},
{name: "Tom", age: 20},
{name: "Larry", age: 30},
{name: "Larry", age: 25}
];

sort_by_property(list, ["name", "age"]);

for (var i = 0; i<list.length; i++) {
console.log(list[i].name + ", " + list[i].age);
}

Output:

  • Bob, 10
  • Larry, 25
  • Larry, 30
  • Tom, 20

How do I sort two arrays relative to each other?

Since you're not dealing individually with only names and grades. I would suggest creating a StudentGrade class for your problem like this:

private static class StudentGrade {
String name;
double grade;

public StudentGrade(String n, double g) {
this.name = n;
this.grade = g;
}
}

You can create an array of StudentGrade[] and sort it with whatever field which fits your use case. I sorted it with name and grade and I was able to see expected output;

StudentGrade[] studentGrades = new StudentGrade[] {
new StudentGrade("Garcia", 2.5),
new StudentGrade("Magsilang", 1.25),
new StudentGrade("Magbag", 1.5)
};

// Sort By Name
System.out.println("(Student Sorted):");
Arrays.sort(studentGrades, Comparator.comparing(o -> o.name));
printStudentGrades(studentGrades);

// Sort by Grade
System.out.println("(Grade Sorted):");
Arrays.sort(studentGrades, Comparator.comparing(o -> o.grade));
printStudentGrades(studentGrades);

This prints this output:

(Student Sorted):
Garcia 2.5
Magbag 1.5
Magsilang 1.25
(Grade Sorted):
Magsilang 1.25
Magbag 1.5
Garcia 2.5

How do I sort two arrays in relation to each other?

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;
}

}

How to sort two arrays with respect to eachother.

Since the two values are so tightly coupled together I would actually write a custom class to contain the information and then sort those classes instead of playing around with raw arrays. Doing so would leave you open to many possible bugs down the line.

This allows for much better control, data encapsulation and future expansion of what methods or data your class may contain.

public class MyDistance implements Comparable<MyDistance> {
private String placename;
private double mileage;

public MyDistance(String placename, double milage) {
this.placename = placename;
this.milage = milage;
}

public String getPlacename() {
return this.placename;
}

public double getMilage() {
return this.milage;
}

@Override
public int compareTo(MyDistance anotherDistance)
{
return milage.compareTo(anotherDistance.getMilage());
}
}

If you want more flexibility in your sort then instead of having your MyDistance class implement Comparable you can write a custom Comparator<MyDistance> class:

public class DistanceComparator extends Comparator<MyDistance> {
@Override
public int compare(MyDistance dist1, MyDistance dist2) {
return dist1.getMilage().compareTo(dist2.getMilage());
}
}

You can use this Comparator to sort using Collections:

List<MyDistance> distanceList = getDistanceListSomehow();
Collections.sort(distanceList, new DistanceComparator());

You are not restricted to a List, I just used it for explanatory purposes. You should look at the full range of Java Collections types to best choose one that suits your purposes. As a suggestion though, the ArrayList type is easy to use and retains order like you would want.

How to sort two arrays with one being sorted based on the sorting of the other?

Pair Class could do the trick here.

import java.util.*;
public class Main
{
static class Pair implements Comparable<Pair>
{
int a1;
int a2;
Pair (int a1, int a2) //constructor
{
this.a1 = a1;
this.a2 = a2;
}
public int compareTo(Pair other) //making it only compare a2 values
{
return this.a2 - other.a2;
}
}
public static void main(String[] args)
{
int[] A1 = {1,2,3,4,5,6,7,8,9,10};
int[] A2 = {1,2,3,0,2,1,1,0,0,0};
Pair[] pairs = new Pair[A1.length];
for (int i = 0; i < pairs.length; i++)
{
pairs[i] = new Pair(A1[i], A2[i]);
}
Arrays.sort(pairs);
//printing values
for (int i = 0; i < A1.length; i++)
{
System.out.print(pairs[i].a1 + " ");
}
System.out.println();
for (int i = 0; i < A2.length; i++)
{
System.out.print(pairs[i].a2 + " ");
}
}
}

By making a Pair class that holds 2 variables a1 and a2, you can override the compareTo method to only compare the a2 value, so that when Arrays.sort is called, the pairs in the Pair array will be swapped only according to the a2 values. You can then access the values in the pairs and print them out. This will produce your desired output.

Sort two arrays based on the length of one

You could take the indices, sort them and map the values for both arrays.

var array1 = ['maximilian', 'moritz', 'hans'],    array2 = [5, 1, 2000],    indices = array1.map((_, i) => i);    indices.sort((a, b) => array1[a].length - array1[b].length);
array1 = indices.map(i => array1[i]);array2 = indices.map(i => array2[i]);
console.log(array1);console.log(array2);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sorting list based on values from another list

Shortest Code

[x for _, x in sorted(zip(Y, X))]

Example:

X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0, 1, 1, 0, 1, 2, 2, 0, 1]

Z = [x for _,x in sorted(zip(Y,X))]
print(Z) # ["a", "d", "h", "b", "c", "e", "i", "f", "g"]

Generally Speaking

[x for _, x in sorted(zip(Y, X), key=lambda pair: pair[0])]

Explained:

  1. zip the two lists.
  2. create a new, sorted list based on the zip using sorted().
  3. using a list comprehension extract the first elements of each pair from the sorted, zipped list.

For more information on how to set\use the key parameter as well as the sorted function in general, take a look at this.



Sort two arrays of different values maintaining original pairing

Blatantly copied from Sorting with map and adapted.

It just uses the same sort order for the other array.

// the array to be sortedvar strings = ['one', 'twooo', 'tres', 'four'],    colors = ['000000', 'ffffff', 'cccccc', '333333'];
// temporary array holds objects with position and sort-valuevar mapped = strings.map(function (el, i) { return { index: i, value: el.length };})
// sorting the mapped array containing the reduced valuesmapped.sort(function (a, b) { return b.value - a.value;});
// container for the resulting ordervar resultStrings = mapped.map(function (el) { return strings[el.index];});var resultColors = mapped.map(function (el) { return colors[el.index];});
document.write('<pre>' + JSON.stringify(resultStrings, 0, 4) + '</pre>');document.write('<pre>' + JSON.stringify(resultColors, 0, 4) + '</pre>');

Sorting two corresponding arrays

Rather than sort the arrays, sort the indices. I.e., you have

int arr[5]={4,1,3,6,2}
string arr1[5]={"a1","b1","c1","d1","e1"};

and you make

int indices[5]={0,1,2,3,4};

now you make a sort indices comparator that looks like this (just and idea, you'll probably have to fix it a little)

class sort_indices
{
private:
int* mparr;
public:
sort_indices(int* parr) : mparr(parr) {}
bool operator()(int i, int j) const { return mparr[i]<mparr[j]; }
}

now you can use the stl sort

std::sort(indices, indices+5, sort_indices(arr));

when you're done, the indices array will be such that arr[indices[0]] is the first element. and likewise arr1[indices[0]] is the corresponding pair.

This is also a very useful trick when you're trying to sort a large data object, you don't need to move the data around at every swap, just the indices.



Related Topics



Leave a reply



Submit