Join the Stack Overflow Community
Stack Overflow is a community of 6.4 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

I have an array of Strings that are instances of a class from external code that I would rather not change.

I also have an array of ints that was generated by calling a function on each object. So I have

A: [string1, string2, string3]

And

B: [40, 32, 34]

How do I easily sort A such that it is sorted in by the values of B. I have boost available. I want to sort A such that it is in the order:

[string2, string3, string1]

In javascript you could do this like:

B.sort(function(a,b){return A[B.indexOf(a)] < A[B.indexOf(b)];});
share|improve this question
3  
Create a custom Comparator that first associates between value in A to value in B (in constructor) and use it to sort – amit Feb 17 '15 at 6:44
1  
Can you not just implement the same thing as your JavaScript example as a Comparator? – Andy Turner Feb 17 '15 at 6:44
1  
You need to write your custom Comparator. – Deepak Tiwari Feb 17 '15 at 6:46
    
You can also use TreeMap (Values in B as key and corresponding value in A as value), given that values in B are unique. – Naman Gala Feb 17 '15 at 6:55

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.

share|improve this answer

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);
  }
}
share|improve this answer
up vote 1 down vote accepted

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;
     }
 }
share|improve this answer

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> stringList = Arrays.asList(strings);
    Collections.sort(stringList, (left, right) -> ints[stringList.indexOf(left)] - ints[stringList.indexOf(right)]);

Or better, with Comparator:

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

    final List<String> stringList = Arrays.asList(strings);
    Collections.sort(stringList, Comparator.comparing(s -> ints[stringList.indexOf(s)]));
share|improve this answer

If you're constructing array B only to be used for this sorting, you can defer calculating it's values within A's compareTo(). In other words, calculate weights of strings only in comparisons during sorting.

share|improve this answer
package com.appkart.array;

import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

public class SortExample {

    Map<String, Integer> map = new HashMap<String, Integer>();
    Map<String, Integer> treemap = new TreeMap<String, Integer>(
            new MyComparator(map));

    public void addValueInMapAndSort() {
        map.put("string1", 40);
        map.put("string2", 32);
        map.put("string3", 34);

        System.out.println(map);
        treemap.putAll(map);
        System.out.println(treemap);
    }


    class MyComparator implements Comparator<String> {

        Map<String, Integer> map;

        public MyComparator(Map<String, Integer> map) {
            this.map = map;
        }

        @Override
        public int compare(String o1, String o2) {
            if (map.get(o1) >= map.get(o2)) {
                return 1;
            } else {
                return -1;
            }
        }
    }

    public static void main(String[] args) {
        SortExample example = new SortExample();
        example.addValueInMapAndSort();
    }
}

Use Comparator for sorting according to value.

share|improve this answer

I had a similar problem, and solved it by coding a sorting algorithm which sorted an array of measures, and made identical swaps in the array of objects. Here is the code, with tests, best wishes and no promises:

package other;

import java.util.Arrays;
import java.util.Random;

/**
 * Sorts an array of objects (<code>bags</code>) by a separate array of doubles (<code>measures</code>). 
 * It sorts into ascending order. 
 * <p>
 * The <code>results</code> array is always a new array. 
 * <p>
 * The algorithm used:<ul>
 * <li> Is (I believe) a merge-sort, which would mean it is stable. (I haven't tested this.)
 * <li> Efficiently exploits already ordered subsequences. 
 * <li> Requires the allocation of eight arrays: four of the baggage type, four of doubles, each the length of the original data. 
 * </ul>
 * <p>
 * A <code>NaN</code> in the <code>measures</code> - I haven't thought about that, and don't want to. 
 * <p>
 * There is test code at the end of the class. 
 */
public class SortBaggageByDouble { 

    public final Object [] results ; 

    protected final int length ; 

    public SortBaggageByDouble(Object[] bags, double[] measures) { 
        this.length = bags.length; 
        if (bags.length!=measures.length) throw new IllegalArgumentException("Mismatched lengths: payload array "+bags.length+", measures array "+measures.length); 
        this.results = new Object[length]; 
        Object [] bagsA = new Object[length] ; 
        Object [] bagsB = new Object[length] ; 
        Object [] bagsC = new Object[length] ; 
        Object [] bagsD = new Object[length] ; 
        double [] measuresA = new double[length] ; 
        double [] measuresB = new double[length] ; 
        double [] measuresC = new double[length] ; 
        double [] measuresD = new double[length] ; 
        System.arraycopy(bags, 0, bagsA, 0, length); 
        System.arraycopy(measures, 0, measuresA, 0, length); 
        munge(length, 0, bagsA, bagsB, bagsC, bagsD, measuresA, measuresB, measuresC, measuresD); 
    }

    private void munge(int inLengthA, int inLengthB, Object[] inBagsA, Object[] inBagsB, Object[] outBagsC, Object[] outBagsD, double[] inMeasuresA, double[] inMeasuresB, double[] outMeasuresC, double[] outMeasuresD) { 
        int outLengthC = 0 ; 
        int outLengthD = 0 ; 
        int cursorA = 0 ; 
        int cursorB = 0 ; 
        boolean toC = true ; 
        while(outLengthC+outLengthD<length) { 
            boolean fromA ; 
            if (cursorA>=inLengthA) { 
                fromA = false ; 
            } else if (cursorB>=inLengthB) { 
                fromA = true ; 
            } else { 
                fromA = inMeasuresA[cursorA] <= inMeasuresB[cursorB] ;  
            } 
            double tmpMeasure = fromA ? inMeasuresA[cursorA] : inMeasuresB[cursorB] ; 
            Object tmpBag = fromA ? inBagsA[cursorA] : inBagsB[cursorB] ; 
            if (fromA) cursorA ++ ; else cursorB ++ ; 
            if (toC) { 
                if (outLengthC==0 || (outMeasuresC[outLengthC-1]<=tmpMeasure)) { 
                    outMeasuresC[outLengthC] = tmpMeasure ; 
                    outBagsC[outLengthC] = tmpBag ; 
                    outLengthC ++ ; 
                } else { 
                    toC = false ; 
                    outMeasuresD[outLengthD] = tmpMeasure ; 
                    outBagsD[outLengthD] = tmpBag ; 
                    outLengthD ++ ; 
                }
            } else { 
                if (outLengthD==0 || (outMeasuresD[outLengthD-1]<=tmpMeasure)) { 
                    outMeasuresD[outLengthD] = tmpMeasure ; 
                    outBagsD[outLengthD] = tmpBag ; 
                    outLengthD ++ ; 
                } else { 
                    toC = true ; 
                    outMeasuresC[outLengthC] = tmpMeasure ; 
                    outBagsC[outLengthC] = tmpBag ; 
                    outLengthC ++ ; 
                }
            }
        }
        if (outLengthC==length) { 
            System.arraycopy(outBagsC, 0, results, 0, length); 
        } else { 
            munge(outLengthC, outLengthD, outBagsC, outBagsD, inBagsA, inBagsB, outMeasuresC, outMeasuresD, inMeasuresA, inMeasuresB); 
        }
    }

    /**
     * Subclass to sort strings, with a result object <code>sortedStrings</code> which is of a useful type. 
     */
    public static class Strings extends SortBaggageByDouble { 

        public final String [] sortedStrings ; 

        public Strings(String[] in, double[] measures) {
            super(in, measures);
            this.sortedStrings = new String[results.length]; 
            for (int i=0 ; i<results.length ; i++) sortedStrings[i] = (String) results[i] ; 
        } 

    }

    /**
     * Tests sorting - assumes there are no duplicates among the measures. 
     */
    private static class NoDuplicatesTest { 
        private NoDuplicatesTest(String[] shuffledStrings, double[] shuffledMeasures, String[] expectedStrings) { 
            SortBaggageByDouble.Strings sorter = new SortBaggageByDouble.Strings(shuffledStrings, shuffledMeasures); 
            if (!Arrays.equals(expectedStrings, sorter.sortedStrings)) throw new RuntimeException("Test failed"); 
        }
    }

    private static class MultiseedNoDuplicatesTest { 
        private MultiseedNoDuplicatesTest(String[] orderedStrings, double[] orderedMeasures, int[] seeds) { 
            int length = orderedStrings.length;
            for (int seed : seeds) { 
                Random random = new Random(seed); 
                int [] shuffleIndices = new int[length] ; 
                for (int i=0 ; i<length ; i++) shuffleIndices[i] = i ; 
                for (int i=1 ; i<length ; i++) { 
                    int j = random.nextInt(i+1); // 'j' is in the range 0..i, bounds inclusive. 
                    int tmp = shuffleIndices[i]; 
                    shuffleIndices[i] = shuffleIndices[j] ; 
                    shuffleIndices[j] = tmp ; 
                }
                String[] shuffledStrings = new String[length]; 
                double[] shuffledMeasures = new double[length]; 
                for (int i=0 ; i<length ; i++) { 
                    shuffledStrings[shuffleIndices[i]] = orderedStrings[i] ; 
                    shuffledMeasures[shuffleIndices[i]] = orderedMeasures[i] ; 
                }
                if (false && 0<length && length<8) { 
                    System.out.println("shuffleIndices is "+ stringfor(shuffleIndices)); 
                    System.out.println("shuffledStrings is "+ stringfor(shuffledStrings)); 
                    System.out.println("shuffledMeasures is "+ stringfor(shuffledMeasures)); 
                }
                new NoDuplicatesTest(shuffledStrings, shuffledMeasures, orderedStrings); 
            }
        }
    }

    private static class MultilengthMultiseedNoDuplicatesTest { 
        MultilengthMultiseedNoDuplicatesTest(int[] lengths, int[] seeds) { 
            for (int i=0 ; i<lengths.length ; i++) { 
                int length = lengths[i] ; 
                String[] orderedStrings = new String[length] ; 
                double[] orderedMeasures = new double[length] ; 
                for (int j=0 ; j<length ; j++) { 
                    orderedStrings[j] = "_"+j+"_" ; 
                    orderedMeasures[j] = j ; 
                }
                if (false && 0<length && length<8) { 
                    System.out.println("orderedStrings is "+ stringfor(orderedStrings)); 
                    System.out.println("orderedMeasures is "+ stringfor(orderedMeasures)); 
                }
                new MultiseedNoDuplicatesTest(orderedStrings, orderedMeasures, seeds); 
            }

        }
    }

    public static class ClassTest { 
        ClassTest() { 
            new MultilengthMultiseedNoDuplicatesTest(new int[]{0}, new int[]{8543, 45125}); 
            new MultilengthMultiseedNoDuplicatesTest(new int[]{1}, new int[]{8543, 45125}); 
            new MultilengthMultiseedNoDuplicatesTest(new int[]{2}, new int[]{8543, 45125, 4545, 785413}); 
            new MultilengthMultiseedNoDuplicatesTest(new int[]{3, 4, 5, 6, 7, 8, 9, 10}, new int[]{8543, 45125, 4545, 785413}); 
            new MultilengthMultiseedNoDuplicatesTest(new int[]{50, 100, 1000}, new int[]{474854, 43233}); 
            //////  Passed! Bye bye.  
            System.out.println("Passed test suite "+this.getClass().getCanonicalName()); 
        }
    }

    public static String stringfor(int[] array) {
        StringBuilder sb = new StringBuilder(); 
        build(sb, array);
        return sb.toString();
    }

    public static void build(StringBuilder sb, int[] array) { 
        for (int i=0 ; i<array.length ; i++) { 
            if (sb.length()>0) sb.append(' '); 
            sb.append(array[i]); 
        } 
    }

    public static String stringfor(double[] array) {
        StringBuilder sb = new StringBuilder(); 
        build(sb, array);
        return sb.toString();
    }

    public static void build(StringBuilder sb, double[] array) { 
        for (int i=0 ; i<array.length ; i++) { 
            if (sb.length()>0) sb.append(' '); 
            sb.append(array[i]); 
        } 
    }

    public static String stringfor(String[] labels) {
        StringBuffer sb = new StringBuffer();
        String sep = "" ; 
        for (int i=0 ; i<labels.length ; i++) { 
            sb.append(sep); 
            String label = labels[i] ; 
            sb.append(label!=null ? label : "null"); 
            sep = ", " ; 
        }
        return sb.toString();
    }

}
share|improve this answer

Make a TreeMap<Integer, List<ObjectTypeFromA>> where the map key is the values in B, and the map values are the values in A (using a list to allow for duplicate keys). It will be sorted in the order of B by definition.

public static void main(String[] args) {
  String[] strings = { "string1", "string2", "string3", "string4" };
  int[] ints = { 40, 32, 32, 34 };
  System.out.println(Arrays.toString(getSortedStringArray(strings, ints)));
}

public static String[] getSortedStringArray(String[] strings, int[] order) {
  Map<Integer, List<String>> map = new TreeMap<>();
  for (int i = 0; i < strings.length; i++) {
    if (!map.containsKey(order[i])) {
      map.put(order[i], new LinkedList<String>());
    }
    map.get(order[i]).add(strings[i]);
  }
  String[] ret = new String[strings.length];
  int i = 0;
  for (Map.Entry<Integer, List<String>> mapEntry : map.entrySet()) {
    for (String s : mapEntry.getValue()) {
      ret[i++] = s;
    }
  }
  return ret;
}
share|improve this answer
2  
What if B has duplicates? – Deepak Tiwari Feb 17 '15 at 6:45
    
I have duplication issue. Treemap isn't working here. – Reza Feb 17 '15 at 6:46
1  
Then make the value a Collection of objects, and create a wrapper that when iterating the tree in order, will also iterate through the contents of each collection. I will update answer shortly. You should probably update question to specify that duplicates are allowed; your example does not bring that possibility to mind. – The111 Feb 17 '15 at 6:51
    
@Reza updated answer to show how this concept still works fine with duplicate keys and a slight modification. – The111 Feb 17 '15 at 7:14
    
@DeepakTiwari updated answer handles duplicates, see int[] ints = { 40, 32, 32, 34 }; – The111 Feb 17 '15 at 7:15

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.