user101
user101

Reputation: 21

Java - Custom sort arrays as if they were tuples

Suppose I have three arrays like such:

a = [1, 2, 1, 4, 1]
b = [6, 1, 2, 4, 3]
c = [9, 6, 4, 7, 8]

I'd like to sort them as if they were a tuple (a, b, c) so the result should look like:

a    |    b    |    c
----------------------
1         2         4
1         3         8
1         6         9
2         1         6
4         4         7

I'd like to write Java code that does this in the most efficient way possible. Thanks!

Upvotes: 2

Views: 145

Answers (2)

Nowhere Man
Nowhere Man

Reputation: 19575

You should create a wrapper object implementing Comparable interface, map the values in the arrays to the instances of this wrapper class, sort, and use the values.

Example:

    static class X implements Comparable<X> {
        final int a;
        final int b;
        final int c;
        public X(int a, int b, int c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }
        
        public int getA() {return a;}
        public int getB() {return b;}
        public int getC() {return c;}
        
        @Override
        public int compareTo(X that) {
            int cmp = Integer.compare(this.a, that.a);
            if (cmp == 0) {
                cmp = Integer.compare(this.b, that.b);
                if (cmp == 0) {
                    cmp = Integer.compare(this.c, that.c);
                }
            }
            return cmp;
        }
    }

    public static void main(String[] args){
        int[] a = {1, 2, 1, 4, 1};
        int[] b = {6, 1, 2, 4, 3};
        int[] c = {9, 6, 4, 7, 8};
        
        IntStream.range(0, a.length)
                 .mapToObj(i -> new X(a[i], b[i], c[i]))
                 .sorted()
                 .forEach(x -> {
                     System.out.printf("%d\t%d\t%d\t\n", x.getA(), x.getB(), x.getC()); 
                 });
    }

This code prints expected output:

1   2   4   
1   3   8   
1   6   9   
2   1   6   
4   4   7

If needed, you can set the sorted values back to the initial arrays.

Update You may also use improved version of the solution offered by Gerold Boser though it has certain limitations on the range of the input integers and/or the count of the arrays:

static int m = 0;

public static void main(String[] args) {
    // range of integer values for 3 arrays is -2^20 ... 2^20 -1
    // or 0..2^21 -1
    final int[] a = { Integer.MAX_VALUE>>11, 1, 1, -4, Integer.MIN_VALUE/(1<<11) };
    final int[] b = { 6, 1, 1, 4, 3 };
    final int[] c = { 9, -6, -24, 7, 8 };

    IntSummaryStatistics stats = 
        Stream.of(Arrays.stream(a), Arrays.stream(b), Arrays.stream(c))
              .flatMapToInt(s -> s)
              .summaryStatistics();
    long min = stats.getMin();
    long max = stats.getMax() - min + 1;

    IntStream.range( 0, a.length )
             // use long to fit multiplication results into 63 bits
             .mapToLong( i -> (a[i] - min) * max * max + (b[i]-min) * max + (c[i]-min) )
             .sorted()
             .forEach( n -> {
                 // re-calculate long back to ints
                 a[m] = (int) (n / (max * max) % max + min);
                 b[m] = (int) (n / max % max + min);
                 c[m] = (int) (n % max + min);
                 System.out.printf("% 9d\t% d\t% d\t\n", a[m], b[m], c[m]);
                 m++;
             });
}

Output:

 -1048576    3   8  
       -4    4   7  
        1    1  -24 
        1    1  -6  
  1048575    6   9

Note The mentioned limitations may be overcome by using BigDecimal instead of long to avoid overflow when performing multiplication

Upvotes: 2

Gerold Broser
Gerold Broser

Reputation: 14782

Try it online!

import static java.lang.System.out;

import java.util.Arrays;
import java.util.stream.IntStream;

public class SO62961100 {

    static int i;

    public static void main( final String[] args ) {

        final int[] a = { 1, 2, 1, 4, 1 };
        final int[] b = { 6, 1, 2, 4, 3 };
        final int[] c = { 9, 6, 4, 7, 8 };

        IntStream.range( 0, a.length )
                .map( i -> a[i] * 100 + b[i] * 10 + c[i] )
                .sorted()
                .forEach( n -> {
                    a[i] = n / 100 % 10;
                    b[i] = n / 10 % 10;
                    c[i++] = n % 10;
                } );
        
        out.printf( "a=%s%nb=%s%nc=%s",
                Arrays.toString( a ),
                Arrays.toString( b ),
                Arrays.toString( c ) );
    }
}

Output

a=[1, 1, 1, 2, 4]
b=[2, 3, 6, 1, 4]
c=[4, 8, 9, 6, 7]

Upvotes: 1

Related Questions