Java - Sort one array based on values of another array?

Solution 1:

In java 8, you can do this

with a lambda:

    String[] strings = new String[]{"string1", "string2", "string3"};
    final int[] ints = new int[]{40, 32, 34};

    final List<String> stringListCopy = Arrays.asList(strings);
    ArrayList<String> sortedList = new ArrayList(stringListCopy);
    Collections.sort(sortedList, (left, right) -> ints[stringListCopy.indexOf(left)] - ints[stringListCopy.indexOf(right)]);

Or better, with Comparator:

    String[] strings = new String[]{"string1", "string2", "string3"};
    final int[] ints = new int[]{40, 32, 34};

    final List<String> stringListCopy = Arrays.asList(strings);
    ArrayList<String> sortedList = new ArrayList(stringListCopy);
    Collections.sort(sortedList, Comparator.comparing(s -> ints[stringListCopy.indexOf(s)]));

Solution 2:

Short answer: I suggest that a separate class is created that holds the information about both the actual String and the boosting (the int). If you assume the following:

public class BoostString {
    int boost;
    String str;

    public BoostString(int boost, String str) {
        this.boost = boost;
        this.str = str;
    }
}

Then, you can sort your array by using a Comparator and it works especially nice with the Java 8 Streaming API.

String[] strings = {"string1", "string2", "string3"};
int[] boosts = {40, 32, 34};

final String[] sorted = IntStream.range(0, boosts.length)
        .mapToObj(i -> new BoostString(boosts[i], strings[i])) // Create the instance
        .sorted(Comparator.comparingInt(b -> b.boost))         // Sort using a Comparator
        .map(b -> b.str)                                       // Map it back to a string
        .toArray(String[]::new);                               // And return an array

The Comparator in the example above is created using the Comparator.comparingInt method which is a convenient way of creating a Comparator for ints using Java 8.


Explanation: Typically when comparing objects in Java you use one of the built-in sorting functions such as Collections.sort where you provide your own Comparator. The Comparator interface is straightforward and looks like this:

public interface Comparator<T> {
    int compare(T o1, T o2);

    // Other default methods for Java 8
}

The return value is of type int and is described like this in the JavaDoc:

return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.

This works out-of-the-box when you are sorting Strings or int (or actually Integers) since they are Comparable – they sort of have a built-in natural sorting and for Strings this is in alphabetical order and for Integers this is sorted in ascending number order (see the JavaDoc for Comparable).

On a side note, there are other "pair" or "tuple" implementations available if you are using 3rd party libraries. You do not have to create your own "pair" of a String and int. One example is the Pair class from Apache Commons.

Solution 3:

You can do something similar to your JS example in old style Java (but I would recommend joining your data together in an object as @wassgren suggests):

import java.util.*;

public class WeightSort {
  public static void main(String[] args) {
    String[] strings = new String[]{"string1", "string2", "string3"};
    final int[] weights = new int[]{40, 32, 34};
    final List<String> stringList = Arrays.asList(strings);
    List<String> sortedCopy = new ArrayList<String>(stringList);
    Collections.sort(sortedCopy, new Comparator<String>(){
        public int compare(String left, String right) {
          return weights[stringList.indexOf(left)] - weights[stringList.indexOf(right)];  
        }
      });
      System.out.println(sortedCopy);
  }
}

Solution 4:

I solved this problem by using Comparator interface.

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


 public class ComparatorDemo {

 public static void main(String[] args) {
    List<Area> metaData = Arrays.asList(
            new Area("Joe", 24),
            new Area("Pete", 18),
            new Area("Chris", 21),
            new Area("Rose",21)
    );

    Collections.sort(metaData, new ResultComparator());
    for(int i =0 ;metaData.size()>i;i++)
             System.out.println(metaData.get(i).output);


  }
 }


 class ResultComparator implements Comparator<Area> {
     @Override
     public int compare(Area a, Area b) {
         return a.result < b.result ? -1 : a.result == b.result ? 0 : 1;
     }
 }

 class Area{
   String output;
   int result;

Area(String n, int a) {
    output = n;
    result = a;
     }
 }

Solution 5:

As @wassgren said, you can use streams, but you don't have to create a class, you can just use indexes:

String[] strings = {"string1", "string2", "string3"};
int[] boosts = {40, 32, 34};

String[] sorted = IntStream.range(0, boosts.length).boxed()
        .sorted(Comparator.comparingInt(i -> boosts[i]))
        .map(i -> strings[i])
        .toArray(String[]::new);

First you create a stream of indexes, then you sort them acording to boosts and then you get the string in that index.