Skip to content
Advertisement

JAVA – Sort Array of Json by multiple values while preserving previous sort order

I have this json and I want to be able to sort it by multiple field values of different types while retaining the previous sort order.

  {
    "code": "1603",
    "description": "",
    "score": 10,
    "max": 100,
    "effts": "2021-12-07T00:00:00",
    "expDate": "2021-06-21",
    "charityMaxCount": 1400000,
    "charityUseCount": 938297,
    "title": "",
    "imageUrl": "",
    "status": "INACTIVE"
  },
  {
    "code": "1604",
    "description": "",
    "score": 10,
    "max": 100,
    "effts": "2020-12-07T00:00:00",
    "expDate": "2021-06-21",
    "charityMaxCount": 1400000,
    "charityUseCount": 938297,
    "title": "",
    "imageUrl": "",
    "status": "INACTIVE"
  },
  {
    "code": "1600",
    "description": "",
    "score": 10,
    "max": 100,
    "effts": "2021-12-07T00:00:00",
    "expDate": "2021-06-21",
    "charityMaxCount": 1400000,
    "charityUseCount": 938297,
    "title": "",
    "imageUrl": "",
    "status": "ACTIVE"
  },
  {
    "code": "1606",
    "description": "",
    "score": 10,
    "max": 100,
    "effts": "2022-12-07T00:00:00",
    "expDate": "2021-06-21",
    "charityMaxCount": 1400000,
    "charityUseCount": 938297,
    "title": "",
    "imageUrl": "",
    "status": "ACTIVE"
  },
  {
    "code": "1601",
    "description": "",
    "score": 10,
    "max": 100,
    "effts": "2020-12-07T00:00:00",
    "expDate": "2021-06-21",
    "charityMaxCount": 1400000,
    "charityUseCount": 938297,
    "title": "",
    "imageUrl": "",
    "status": "ACTIVE"
  }
]

The fields by which I should decide to sort it are given through another json:

  {
    "id": 1,
    "field": "status",
    "type": "string",
    "sortMode": "ASC"
  },
  {
    "id": 2,
    "field": "expDate",
    "type": "string",
    "sortMode": "ASC"
  }
]

I managed to achieve this by converting the json array to a list of Hashmap<String,Object> and wrote a custom comparator. The problem is when I sort by status in ASC order which puts the ACTIVE entries at top, I don’t want this order to get messed when I choose to sort it by another field like expDate for example.

Here is my code:

    public String sort() throws JsonProcessingException {
        List<TreeMap<String, String>> sortOrder = readSortOrder(sortOrderJson);
        List<HashMap<String, Object>> payLoad = readPayLoad(payloadJson);
        ObjectMapper objectMapper = new ObjectMapper();
        if (sortOrder.size() > 1) {
            for (TreeMap<String, String> map : sortOrder) {
                if (map.get(Constants.SORT_MODE).equals(SortOrder.DSC.toString())) {
                    payLoad.sort(new MapComparator(map.get(Constants.FIELD)).reversed());
                } else {
                    payLoad.sort(new MapComparator(map.get(Constants.FIELD)));
                }
            }
        }
        return objectMapper.writeValueAsString(payLoad);
    }

    public List<HashMap<String, Object>> readPayLoad(String jsonInput) {
        ObjectMapper mapper = new ObjectMapper();
        List<HashMap<String, Object>> jsonList = new ArrayList<>();
        try {
            HashMap[] payLoadDTOS = mapper.readValue(jsonInput, HashMap[].class);
            jsonList = Arrays.asList(payLoadDTOS);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return jsonList;
    }

    public List<TreeMap<String, String>> readSortOrder(String sortOrder) {
        final ObjectMapper objectMapper = new ObjectMapper();
        List<TreeMap<String, String>> jsonList = new ArrayList<>();
        try {
            TreeMap[] sortOrderDTOS = objectMapper.readValue(sortOrder, TreeMap[].class);
            jsonList = Arrays.asList(sortOrderDTOS);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return jsonList;
    }

And this as comparator:

public class MapComparator implements Comparator<Map<String, Object>> {

    private final String key;


    public MapComparator(String key) {
        this.key = key;
    }


    @Override
    public int compare(Map<String, Object> one, Map<String, Object> two) {
        Object first = one.get(key);
        Object second = two.get(key);
        if (first instanceof String && second instanceof String) {
            if ((isValidDate((String) first) && isValidDate((String) second))
                    || (isValidDateTime((String) first) && isValidDateTime((String) second))) {
                return DateTimeComparator.getInstance().compare(first, second);
            } else {
                return ((String) first).compareTo((String) second);
            }
        } else if (first instanceof Integer && second instanceof Integer) {
            return ((Integer) first).compareTo((Integer) second);
        }
        return 0;
    }
}

I think I should separate say ACTIVE and INACTIVE in two other collections and sort each then, but the fields by which I may conduct the separation could be dynamic and may change each time. How can I algorithmically handle this?

Advertisement

Answer

A sorting algorithm that preserves the existing order on same-valued entries is called stable. In Java, you can consult the API whether a given sorting function is guaranteed to be stable, such as Arrays.sort.

The typical way of sorting using multiple keys is to sort the entries sequentially with a stable sorting algorithm in reverse order of keys.

For example, if you want to order by first name first and last name second, you would first sort by last name and then by first name.

You also need to make sure that the data structure you use preserves the order of insertion, for example a Set or Map may not preserve that.

Advertisement