Paul Stoner
Paul Stoner

Reputation: 1512

Java Sorting columns in 2D Array of Strings

I have been working off of this SO question. However, my data is arranged slightly differently.

Without going into HOW the data is retrieved, suffice it to say the resulting array looks like this

String[2][]
    [0] = String[4]
    [1] = String[4]

The actual data, then is as follows

data = String[2][]
  data[0]
    data[0][0] = "Columbia"
    data[0][1] = "Chile"
    data[0][2] = "Asia"
    data[0][3] = "US"
  data[1]
    data[1][0] = "B216"
    data[1][1] = "B217"
    data[1][2] = "A442"
    data[1][3] = "N665"

I want to sort the entire array by data[0] in alphabetical order. Yet I realized that the solution provided in the referenced SO question is working off of a different array format.

My expected result would look like

data = String[2][]
  data[0]
    data[0][0] = "Asia"
    data[0][1] = "Chile"
    data[0][2] = "Columbia"
    data[0][3] = "US"
  data[1]
    data[1][0] = "A442"
    data[1][1] = "B217"
    data[1][2] = "B216"
    data[1][3] = "N665"

I'm not entirely certain how to obtain these results without iterating over each element and shifting them to a new array.

Any ideas?

Upvotes: 2

Views: 1424

Answers (5)

Ritesh
Ritesh

Reputation: 1

package com.mycompany.stringsortingby2darray;

public class StringSortingby2dArray {

    public static void main(String[] args) {

        String data[][]= {{"MADHYAPRADESH",String.valueOf(1)},{"BHARAT",String.valueOf(2)},{"usa",String.valueOf(13)},{"Australia",String.valueOf(17)},{"UTTARPRADESH",String.valueOf(5)}};

        for (int i = 0; i < data.length; i++) {
            //for (int j = 0; j < data.length; j++) {
                System.out.println(data[i][0]);
            }
        }

    }
}

Upvotes: 0

Mad Physicist
Mad Physicist

Reputation: 114230

I can see three options for how to sort multiple arrays into the same order:

  1. Create an array of indices, sort it using a Comparator that refers to the array you want to sort by based on the index, then rearrange the arrays according to the sorted indices. Here is a sample:

    Integer[] indices = new Integer[data[0].length];
    for(int i = 0; i < data[0].length; i++)
        indices[i] = i;
    Arrays.sort(indices, new Comparator<Integer>() {
        @Override
        public int compare(Integer i1, Integer i2) {
            return data[0][i1].compareTo(data[0][i2]);
        }
    });
    

    The index array has to be of type Integer rather than int because Arrays does not allow sorting primitives with a custom comparator. You can then rearrange both arrays by the index array doing something similar to the function below. I can't think of a way to do it in place without messing up the order and invalidating the index array.

    public String[] rearrange(Integer[] indices, String[] input) {
        String[] output = new String[];
        for(int i = 0; i < indices.length; i++)
            output[i] = input[indices[i]];
        return output;
    }
    

    This function does no error checking to see if the inputs are the same length and it doesn't use streams, which I am sure it can be rewritten to do. It is just an example of the concept. To use it:

    for(int i = 0; i < data.length; i++)
        data[i] = rearrange(indices, data[i]);
    

    This is probably the least memory-intensive option for rearranging the arrays. It is inspired by this answer to the question that I think is a duplicate of yours.

  2. Create an object that holds all the elements at a given index and make it Comparable. This is the solution to the accepted answer for the question referenced previously.

    public class Container implements Comparable<Container>
    {
        public final String country;
        public final String code;
    
        public Container(Sting country, String code)
        {
            this.country = country;
            this.code = code;
        }
    
        public int compareTo(Container other)
        {
            return this.country.compareTo(other.country);
        }
    }
    

    You would have to transform your data into an array of these containers and then sort it:

    objects = new Container[data[0].length];
    for(int i = 0; i < objects.length; i++)
        objects[i] = new Container(data[0][i], data[1][i]);
    Arrays.sort(objects);
    

    While this method does require copying the data over into a different format, it is actually more robust than working with an array of arrays because it groups the conceptually related items into a single item. This completely eliminates the need to check for things like array lengths being equal, as well as being generally much more object oriented. You could even rewrite your data input to just spit out an array or List of Container objects instead of a 2D array.

  3. Create an explicit mapping between the countries and the codes, then dereference the values based on the sorted keys. This is sort of what #1 does, but it will allow you to at least sort one of the arrays in-place. If the mapping is an ordered mapping, you can just turn it into the sorted arrays directly.

    a. Unsorted mapping:

    HashMap<String, String> mapping = new HashMap<>();
    for(int i = 0; i < data[0].length; i++)
        mapping.put(data[0][i], data[1][i]);
    Arrays.sort(data[0]);
    
    String[] outputCodes = new String[data[1].length];
    for(int i = 0; i < outputCodes.length; i++)
        outputCodes[i] = mapping(data[0][i]);
    data[1] = outputCodes;
    

    b. Sorted mapping:

    TreeMap<String, String> mapping = new TreeMap<>();
    for(int i = 0; i < data[0].length; i++)
        mapping.put(data[0][i], data[1][i]);
    data[0] = new String[mapping.size()];
    data[1] = new String[mapping.size()];
    int index = 0;
    for(Map.Entry<String, String> entry : mapping.entrySet()) {
        data[0][index] = entry.getKey();
        data[1][index] = entry.getValue();
        index++;
    }
    

    These methods both have the disadvantage that they only really work well for two columns of data. If you have more than that, your values have to become custom objects or arrays of objects or something similar. Either way, this method is pretty clunky and I am only providing it to illustrate how you can jump through extra hoops if you really want to.

There are probably other ways, but as #3 demonstrates, I suspect that they will not be particularly efficient in terms of speed, memory or legibility/maintainability compared to the three shown here.

Also keep in mind that streams will let you do everything I have shown you here much easier.

Upvotes: 1

Paul Stoner
Paul Stoner

Reputation: 1512

Solution

Taking the advice of Socowi and Stephen P and Sorting multiple arrays simultaneously I came up with the following solution

As I am returning this data to a JSP to populate a drop down, I added a class for the model and iterated over the array adding a new instance of the model class to an Array List

for (int i = 0; i < data[0].length; i++) {
  dataList.add(new LocationModel(data[0][i], data[1][i].trim()));
}

The model class implements Comparable and overrides the compareTo method

public class LocationModel implements Comparable<LocationModel> {
  ...
  @Override
    public int compareTo(LocationModel other) {
        return this.locName.compareToIgnoreCase(other.getLocName());
    }
}

Then I simply sort the Array List

Collections.sort(dataList);

This has solved my question.

Thanks everyone for your assistance

Upvotes: 0

Devendra Lattu
Devendra Lattu

Reputation: 2802

Complete code here

  1. Sort the internal String[] of data.
  2. Add the data to an ArrayList

    List<String[]> list = new ArrayList<>();
    // Sort the String array and insert it in List
    for (String dataArr[] : data) {
        String s[] = dataArr.clone(); // create new object of String array
        Arrays.sort(s); // Sort newly created String array
        list.add(s);
    }
    
  3. Use custom comparator to sort this list.
    3.1 Keep in mind that you need to scan through the entire String[] of the two arrays in comparision for finding the first difference.

    // Sort the list using custom comparator
    Collections.sort(list, new Comparator<String[]>() {
    
        @Override
        public int compare(String[] o1, String[] o2) {
    
            // optional: condition to check for string length equality
            if (o1.length == o2.length) {
                for (int i = 0; i < o1.length; i++) {
                    if (o1[i].equals(o2[i])) { // ** IMP **
                        // allows scanning through entire string array.
                        continue;
                    }
                    // Find the first different strings in both arrays
                    return o1[i].compareTo(o2[i]);
                }
            } else if (o1.length < o2.length) {
                // allow string arrays with lesser elements to appear first
                return -1;
            } else if (o1.length > o2.length) {
                return 1;
            }
    
            // When o1.length == o2.length and all strings are equal
            return 0; // no difference
        }
    
    });
    

Sample output for data (first line) and the sorted list enter image description here

Upvotes: 1

strash
strash

Reputation: 1321

Try that. Works perfect for me.

        Map<String, String> unsortMap = new HashMap<>();
                unsortMap.put("Columbia", "B216");
                unsortMap.put("Chile", "B217");
                unsortMap.put("Asia", "A442");
                unsortMap.put("US", "N665");

                System.out.println("Original...");
                System.out.println(unsortMap);

                Map<String, String> result = new LinkedHashMap<>();

                //sort by key, a,b,c..., and put it into the "result" map
                unsortMap.entrySet().stream()
                        .sorted(Map.Entry.<String, String>comparingByKey())
                        .forEachOrdered(x -> result.put(x.getKey(), x.getValue()));

                System.out.println("Sorted...");
                System.out.println(result);

Upvotes: 0

Related Questions