Создание составного компаратора во время выполнения для сортировки - PullRequest
0 голосов
/ 10 мая 2019

Мой пример запроса

 {
  "requestModel":{
      "CUSTID": "100"
     },
  "returnParameters":[
    {
     "name":"NETWORK/NETID",
     "datatype":"String",
     "order":"asc",
     "sequence":1
    },
    {
     "name":"INFODATA/NAME",
     "datatype":"String",
     "order":"asc",
     "sequence":1
    },
    {
     "name":"SOURCE/SYSTEM",
     "datatype":"int",
     "order":"asc",
     "sequence":2
    },
   ]
 }

Пример ответа

Ниже приведен мой динамически генерируемый формат карты ответа json [Параметры ответа будуткаждый раз различается в зависимости от параметров запроса],

"responseModel":{
  "documents": [
{
 "NETWORK":[
    {"NETID":"1234"},
    {"ACT":"300"}
   ],
   "SOURCE": {
      "SYSTEM":"50"
     },
   "INFODATA":{
     "NAME":"PHIL"
     }
 },
 {
  "NETWORK":[
    {"NETID":"1234"},
    {"ACT":"300"}
   ],
   "SOURCE": {
      "SYSTEM":"100"
     },
   "INFODATA":{
     "NAME":"PHIL"
     }
  }
 ]
}

Постановка задачи

Мне нужно выполнить многоуровневую сортировку на основе «returnParameters» в запросе , который является динамическим ... «порядок» указывает на возрастание (или) по убыванию, а последовательность указывает приоритет для упорядочения, как (группировать по SQL-запросу)

код

Map<String,Object> documentList = new HashMap<String,Object>();
JSONObject jsonObject= new JSONObject(response.getContent());

response.getContent () -> ничего, но он содержит ответ json в формате карты.

Now I converting the map to list of json object

JSONArray jsonArray= (JSONArray)jsonObject.get("documents");
ArrayList<JSONObject> list = new ArrayList<>();
for(int i=0;i<jsonArray.length();i++){
 list.add((JSONObject) jsonArray.get(i));
 }
 Collections.sort(list, new ResponseSorter());
 public class ResponseSorter implements Comparator<JSONObject> {
  @Override
  public int compare(JSONObject o1,JSONObject o2){
  String s1= (String)((JSONObject) o1.get("NETWORK")).get("NETID");
  String s2= (String)((JSONObject) o2.get("NETWORK")).get("NETID");
  int i1=Integer.parseInt(s1);
  int i2=Integer.parseInt(s2);
  return i1-i2;
  }
 }

Я застрял здесь, чтобы продолжить.Создан один для целочисленного компаратора. Я должен создать для каждого типа данных?Также мне нужно динамически построить составной компаратор путем анализа «retunrParameters», ниже образец жестко запрограммирован, как создать динамически ??

(String)((JSONObject) o1.get("NETWORK")).get("NETID"); -> this should be dynamically framed , since "returnParameters" are also dynamic in nature.[NETWORK & NETID may not be come in another request],so my comparator should be capable enough to frame the keys in runtime

Может ли кто-нибудь помочь мне создать составной компаратор во время выполнения длясортировка?

ПРИМЕЧАНИЕ: - Java Pojo не может быть создан, поскольку ответ имеет динамический характер

Ответы [ 3 ]

1 голос
/ 10 мая 2019

ИЗМЕНЕНО после дополнительных вопросов в комментариях и дополнительной информации в описании


У вас есть пара шагов, которые необходимо сделать здесь, чтобы найти решение:

  1. Вы хотите, чтобы сортировка была динамической на основе значения свойства sequence в запросе. Поэтому вам нужно разобрать имена этих returnParameters и привести их в порядок. Ниже я сопоставляю их с List, где каждая строка [] имеет name и order (asc / desc). Список будет упорядочен с использованием значения sequence:

    List<String[]> sortParams = params.stream() // params is a List<JSONObject>
            .filter(json -> json.containsKey("sequence")) // filter those that have "sequence" attribute
            .sorted( sequence ) // sorting using Comparator called sequence
            .map(jsonObj -> new String[]{jsonObj.get("name").toString(), jsonObj.get("order").toString()} )
            .collect(Collectors.toList());
    

Перед этим вы сначала отобразите объекты в массиве returnParameters в запросе в List. Затем поток обрабатывается путем 1. фильтрации объектов JSONObject, чтобы сохранить только те из них, которые имеют prop sequence, 2. sort JSONObjects, используя компаратор ниже. 3. из каждого JSONObject получите «имя» и «порядок» и поместите их в строку [], 4. сгенерируйте список с этими массивами. Этот список будет упорядочен в порядке атрибутов с приоритетом 1, а затем с приоритетом 2 и т. Д., Поэтому он будет упорядочен так же, как вы хотите, чтобы объекты JSONObject были упорядочены в конце.

    Comparator<JSONObject> sequence = Comparator.comparingInt(
        jsonObj -> Integer.valueOf( jsonObj.get("sequence").toString() ) 
    );

Итак, для вашего примера sortParams будет выглядеть так: List( String[]{"NETWORK/NETID", "asc"}, String[]{""INFODATA/NAME", "asc"}, String[]{"SOURCE/SYSTEM", "asc"} )

  1. Затем вам нужно написать метод, который принимает два параметра: JSONObject и String (путь к свойству) и возвращает значение этого свойства. Первоначально я посоветовал вам использовать интерфейс JSONAware, а затем выяснить подкласс, но давайте пока об этом забудем.

    Я не собираюсь писать этот метод для вас. Просто помните, что .get(key) метод JSON.Simple всегда дает Object. Напишите метод с такой подписью:

    public String findSortValue(JSONObject doc, String path){
        // split the path
        // find the parent
        // cast it  (parent was returned as an Object of type Object)
        // find the child
        return value;
    }
    
  2. Напишите общий индивидуальный компаратор (который сравнивает значения только одного атрибута сортировки за раз) и выясняет, является ли это Int, Date или обычной строкой. Я бы написал это как обычный метод, чтобы потом было проще все комбинировать. Поскольку у вас было так много вопросов по этому поводу, я сделал пример:

    int individualComparator(String s1, String s2){
    int compResult = 0;
    try{
        int numeric1 = Integer.parseInt(s1);
        int numeric2 = Integer.parseInt(s2);
        compResult = numeric1 - numeric2; // if this point was reached both values could be parsed
    } catch (NumberFormatException nfe){
        // if the catch block is reached they weren't numeric
        try{
            DateTime date1 = DateTime.parse(s1);
            DateTime date2 = DateTime.parse(s2);
            compResult = date1.compareTo(date2); // compareTo method of joda.time, the library I'm using
        } catch (IllegalArgumentException iae){
            //if this catch block is reached they weren't dates either
            compResult = s1.compareTo(s2);
        }
    }
    return compResult;
    

    };

    1. Напишите общий компаратор, который объединяет все

      Comparator<JSONObject> overAllComparator = (jsonObj1, jsonObj2) -> {
          List<String[]> sortValuesList = sortParams.stream()
              .map(path -> new String[]{ findValueByName(jsonObj1, path), findValueByName(jsonObj2, path) } )
              .collect(Collectors.toList());
      
      //assuming we always have 3 attributes to sort on
      int comp1 = individualComparator(sortValuesList.get(0)[0], sortValuesList.get(0)[1]);
      int comp2 = individualComparator(sortValuesList.get(1)[0], sortValuesList.get(1)[1]);
      int comp3 = individualComparator(sortValuesList.get(2)[0], sortValuesList.get(2)[1]);
      
      int result = 0;
      if (comp1 != 0){
          result = comp1;
      } else if (comp2 != 0){
          result = comp2;
      } else{
          result = comp3;
      }
      return result;
      };
      

Этот компаратор написан в лямбда-стиле, для получения дополнительной информации https://www.mkyong.com/java8/java-8-lambda-comparator-example/.

Сначала он берет упорядоченный список sortParams, который мы создали на шаге 1, и для каждого возвращает массив, где позиция 0 имеет значение для jsonObj1, а позиция 1 имеет значение для jsonObj2 и собирает его в sortValuesList , Затем для каждого атрибута, по которому производится сортировка, он получает результат метода individualComparator. Затем он идет по линии и возвращает в результате общего сравнения первое, которое не приводит к 0 (когда компаратор дает 0, оба значения равны).

Единственное, чего сейчас не хватает, так это значения asc / desc из запроса. Вы можете добавить это, связав int comp1 = individualComparator(sortValuesList.get(0)[0], sortValuesList.get(0)[1]); простым методом, который принимает int & a String и умножает int на -1, если String равна "desc". (Помните, что в sortParams мы добавили значение для order в позицию 1 массива).

Поскольку первый список, который мы составили, sortParams был упорядочен в соответствии с приоритетом, указанным в запросе, и мы всегда делали все в порядке следования этому списку, результатом является мультисортировка в этом порядке. Он является общим и будет определяться динамически содержимым returnParams в запросе. Вы можете применить его к своему списку JSONObjects, используя Collections.sort()

1 голос
/ 13 мая 2019

В вашем случае простой компаратор, который снабжен параметрами сортировки, может быть проще для понимания, чем набор вложенных компараторов.

По сути, вы бы сделали что-то вроде этого:

class ReturnParameterComparator implements Comparator<JSONObject> {
   private List<ReturnParameter> params; //set via constructor

   public int compare( JSONObject left, JSONObject right) {
     int result = 0;
     for( ReturnParameter p : params ) {
       //how exactly you get those values depends on the actual structure of your data and parameters
       String leftValueStr = left.get( p ); 
       String rightValueStr = right.get( p ); 

       switch( p.datatype ) {
         case "String": 
           result = String.compare( leftValueStr, rightValueStr );
           break;
         case "int": 
           //convert and then compare - I'll leave the rest for you 
       }

       //invert the result if the order is descending
       if( "desc".equals(p.order ) {
         result += -1;
       }

       //the values are not equal so return the order, otherwise continue with the next parameter
       if( result != 0 ) {
         return result;
       }
     }

     //at this point all values are to be considered equal, otherwise we'd have returned already (from the loop body)
     return 0;
   }
}

Обратите внимание, что это всего лишь заглушка для начала.Вам нужно будет добавить несколько вещей:

  • как правильно использовать параметры для извлечения значений из объектов json
  • как преобразовать данные на основе типа
  • как обрабатывать нулевые, отсутствующие или несовместимые данные (например, если значение должно быть отсортировано как «int», но его нельзя проанализировать)

Добавление всех этих данных также будет слишкоммного для объема этого вопроса и зависит от ваших данных и требований в любом случае.

0 голосов
/ 10 мая 2019

Мое предложение: узнайте о:

  • Comparator.comparing, который позволяет создать компаратор, указав ключевой экстрактор
  • Comparator.thanComparing, который позволяет объединять несколько компараторов.Позднее в цепочке компараторы вызываются, только если предшественники говорят, что объекты равны

Учебное пособие, если вам нужно: https://www.baeldung.com/java-8-comparator-comparing

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...