0

I have 3 lists:

ArrayList<Integer> at = new ArrayList<>(Arrays.asList(2, 0, 2, 3, 4));
ArrayList<Integer> bt = new ArrayList<>(Arrays.asList(2, 1, 3, 5, 4)); 
ArrayList<String> prc = new ArrayList<>(Arrays.asList("p1", "p2", "p3", "p4", "p5"));

If I sort at, I'll get [0, 2, 2, 3, 4].
I want both bt and prc to follow this pattern. i.e: bt =[1, 2, 3, 5, 4] and prc=["p2", "p1", "p3", "p4", "p5"]. Since at at index 1 got swapped with index 2 upon sort, I want the same thing to happen with the other 2 arrays as well.

I tried implementing this in Python. It was easy because, if the values are in a data frame, sorting one column will automatically give the required result. I couldn't do the same in Java.

Matt
  • 12,848
  • 2
  • 31
  • 53

2 Answers2

2

Method 1 (extract indexes first and construct new list)

If you can change the data format and use a custom object, I recommend to use method 2.

You can first extract the indexes of the original elements in at into an array sortedIndices. If the list is already sorted, the value would be [1, 2, 3, ..., n-1]. If the first two elements need to be swapped, the value would be [1, 0, 2, 3, ..., n-1]. Afterwards, you can construct and populate the two sorted lists (btSorted/prcSorted) by adding the elements according to the position stored sortedIndices[i].

ArrayList<Integer> at = new ArrayList<>(Arrays.asList(2, 0, 2, 3, 4));
ArrayList<Integer> bt = new ArrayList<>(Arrays.asList(2, 1, 3, 5, 4));
ArrayList<String> prc = new ArrayList<>(Arrays.asList("p1", "p2", "p3", "p4", "p5"));

// get indexes of a sorted 'at' list
int[] sortedIndices = IntStream.range(0, at.size())
        .boxed().sorted(Comparator.comparing(at::get))
        .mapToInt(x -> x).toArray();

// sort 'bt' according to 'at'
ArrayList<Integer> btSorted = new ArrayList<>();
for (int i = 0; i < sortedIndices.length; i++) {
    btSorted.add(bt.get(sortedIndices[i]));
}

// sort 'prc' according to 'at'
ArrayList<String> prcSorted = new ArrayList<>();
for (int i = 0; i < sortedIndices.length; i++) {
    prcSorted.add(prc.get(sortedIndices[i]));
}

// sort 'at' directly (just for inspection)
ArrayList<Integer> atSorted = new ArrayList<>(at);
Collections.sort(atSorted);

System.out.println("idx: " + Arrays.toString(sortedIndices));
System.out.println("at:  " + at + " => " + atSorted);
System.out.println("bt:  " + bt + " => " + btSorted);
System.out.println("prc: " + prc + " => " + prcSorted);

Output:

idx: [1, 0, 2, 3, 4]
at:  [2, 0, 2, 3, 4] => [0, 2, 2, 3, 4]
bt:  [2, 1, 3, 5, 4] => [1, 2, 3, 5, 4]
prc: [p1, p2, p3, p4, p5] => [p2, p1, p3, p4, p5]

Method 2 (custom class and Comparator)

Much more readable would be to store the data in a custom class (e.g. DataEntry). Then, you can easily sort the array of objects (e.g. data) without resorting to hard to read code:

ArrayList<DataEntry> data = new ArrayList<>();
data.add(new DataEntry(2, 2, "p1"));
data.add(new DataEntry(0, 1, "p2"));
data.add(new DataEntry(2, 3, "p3"));
data.add(new DataEntry(3, 5, "p4"));
data.add(new DataEntry(4, 4, "p5"));

// sort data by 'at'
data.sort(Comparator.comparing(d -> d.at));

// print result
data.forEach(d -> System.out.println(d.at + ", " + d.bt + ", " + d.prc));

Output:

0, 1, p2
2, 2, p1
2, 3, p3
3, 5, p4
4, 4, p5

DataEntry class for reference:

static class DataEntry {
    int at;
    int bt;
    String prc;

    public DataEntry(int at, int bt, String prc) {
        this.at = at;
        this.bt = bt;
        this.prc = prc;
    }
}

Method 3 (with indexOf and Comparator)

This is only stable when there are no duplicates in the lists to be sorted (bt/prc) since indexOf always returns the index of the first occurrence of the specified element in a list (or -1).

Sorting the lists could be done with a custom Comparator and indexOf. We can refer to the values in at at the respective position of the current element being compared (d) in the other lists (bt/prc) in order to sort them:

ArrayList<Integer> at = new ArrayList<>(Arrays.asList(2, 0, 2, 3, 4));
ArrayList<Integer> bt = new ArrayList<>(Arrays.asList(2, 1, 3, 5, 4));
ArrayList<String> prc = new ArrayList<>(Arrays.asList("p1", "p2", "p3", "p4", "p5"));

// sort 'bt' according to 'at'
ArrayList<Integer> btSorted = new ArrayList<>(bt);
btSorted.sort(Comparator.comparing(d -> at.get(bt.indexOf(d))));

// sort prc according to 'at'
ArrayList<String> prcSorted = new ArrayList<>(prc);
prcSorted.sort(Comparator.comparing(d -> at.get(prc.indexOf(d))));

// sort 'at' (just for inspection)
ArrayList<Integer> atSorted = new ArrayList<>(at);
Collections.sort(atSorted);

// print result
System.out.println(at + " => " + atSorted);
System.out.println(bt + " => " + btSorted);
System.out.println(prc + " => " + prcSorted);

Output:

at:  [2, 0, 2, 3, 4] => [0, 2, 2, 3, 4]
bt:  [2, 1, 3, 5, 4] => [1, 2, 3, 5, 4]
prc: [p1, p2, p3, p4, p5] => [p2, p1, p3, p4, p5]
Matt
  • 12,848
  • 2
  • 31
  • 53
0

You have 2 options. The first is kind of obvious and I am curious as why it might not be an option for you.

Option 1)

Create a class containing values for at, bt, prc implementing Comparable where compareTo(Foo other) returns at-other.at;

Option 2)

Create a new array containing indexes like:

    ArrayList<Integer> indexes = List.of(0,1,2,3,4);
    indexes.sort(new Comparator(Integer o1, Integer o2) {
       return at[o1.intValue()]-at[o2.intValue()];
    })

The resulting list will be something like {1,0,2,3,4} then:

    ArrayList<Integer> newAt = new ArrayList<>()
    ArrayList<Integer> newBt = new ArrayList<>()
    ArrayList<String> newPrc = new ArrayList<>()

    for(Integer i : indexes) {
      newAt.add(at.get(i));
      newBt.add(bt.get(i));
      newPrc.add(prc.get(i));
    }
jhhdk
  • 29
  • 3
  • Ohh well great minds think alike, ignore my post first post is exactly the same but written better. – jhhdk Jan 09 '21 at 14:55