简体   繁体   English

在运行时创建Composite Comparator进行排序

[英]Creating Composite Comparator in runtime for Sorting

My sample request 我的样品要求

 {
  "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
    },
   ]
 }

Sample Response 样品响应

Below is my dynamically generated Map format of json response[Response parameters will be different each time based on the request params], 下面是我动态生成的json响应的Map格式[响应参数根据请求参数的不同,每次都会有所不同],

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

Problem Statement 问题陈述

I need to do multi level sorting based on the "returnParameters" in the request which is dynamic ... "order" indicates ascending (or) descending and sequence indicates the the priority for ordering like (group by in sql query) 我需要基于动态请求中的“ returnParameters”进行多级排序...“ order”表示升序(或)降序,sequence表示排序的优先级,例如(在sql查询中为group by)

Code

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

response.getContent() -> is nothing but it contains the above json response in Map format. response.getContent()->没什么,但是它包含以上以Map格式的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;
  }
 }

I'm stuck here to proceed further. 我被困在这里以继续进行。 Created one for Integer comparator, .Should I create for each dataType? 为Integer比较器创建了一个,是否应该为每个dataType创建一个? also I need to dynamically construct the composite comparator by parsing the "retunrParameters" , below sample is hard coded, how to create dynamically?? 我还需要通过解析“ 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

Would anyone able to assist me to create composite comparator in runtime for sorting? 有人能协助我在运行时创建复合比较器进行排序吗?

NOTE:- Java Pojo cannot be created as the response is dynamic nature 注意:-无法创建Java Pojo,因为响应是动态的

EDITED after additional questions in comments and additional info in description 在评论中的其他问题和描述中的其他信息之后进行编辑


You have a couple of steps you need to do here to get to the solution: 您需要执行以下几个步骤才能找到解决方案:

  1. You want to have the sorting be dynamic based on the value of the property sequence in the request. 您希望基于请求中属性sequence的值进行动态排序。 So you need to parse the names of those returnParameters and put them in order. 因此,您需要解析这些returnParameters的名称, returnParameters顺序排列它们。 Below I map them to a List where each String[] has the name and order (asc/desc). 在下面,我将它们映射到一个List,其中每个String []都具有nameorder (asc / desc)。 The list will be ordered using the value of sequence : 该列表将使用sequence的值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()); 

Before this you'll map the objects in the returnParameters array in the request to a List first.Then the stream is processed by 1. filtering the JSONObjects to only keep those that have prop sequence , 2. sorting the JSONObjects using comparator below. 在此之前,您首先将请求中returnParameters数组中的对象映射到一个List。然后通过1.过滤JSONObject以仅保留具有prop sequence的JSONObject进行处理,2.使用下面的比较器对JSONObject进行排序。 3. from each JSONObject get "name" & "order" and put them in a String[], 4. generate a list with those Arrays. 3.从每个JSONObject中获取“名称”和“顺序”,并将它们放入String [],4.生成包含这些数组的列表。 This list will be ordered in the order of attributes with priority 1 first, then priority 2, etc, so it will be ordered in the same way you want the JSONObjects ordered in the end. 该列表将按照属性的顺序进行排序,优先级为1,然后是优先级2,依此类推,因此,其排序方式与您希望JSONObjects最终排序的方式相同。

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

So for your example, sortParams would look like: List( String[]{"NETWORK/NETID", "asc"}, String[]{""INFODATA/NAME", "asc"}, String[]{"SOURCE/SYSTEM", "asc"} ) 因此,对于您的示例,sortParams将类似于: List( String[]{"NETWORK/NETID", "asc"}, String[]{""INFODATA/NAME", "asc"}, String[]{"SOURCE/SYSTEM", "asc"} )

  1. Then you need to write a method that takes two params: a JSONObject and a String (the path to the property) and returns the value of that property. 然后,您需要编写一个接受两个参数的方法:JSONObject和String(属性的路径)并返回该属性的值。 Originally I advised you to use JSONAware interface and then figure out the sub-class, but let's forget about that for now. 最初,我建议您使用JSONAware接口,然后找出该子类,但现在我们暂时不要再说了。

    I am not going to write this method for you. 我不会为您编写此方法。 Just keep in mind that .get(key) method of JSON.Simple always yields an Object . 只需记住JSON.Simple的.get(key)方法始终会产生一个Object Write a method with this signature: 用此签名编写一个方法:

     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. Write a generic individual comparator (that compares values of just one sort attribute at a time) and figures out if it's an Int, Date or regular String. 编写一个通用的单个比较器(一次只比较一个sort属性的值),并弄清楚它是Int,Date还是常规String。 I would write this as a regular method so it'll be easier to combine everything later on. 我将其编写为常规方法,以便以后将所有内容组合起来会更容易。 Since you had so many questions about this I've made an example: 由于您对此有很多疑问,因此我举了一个例子:

     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. Write an overall Comparator that combines everything 编写一个综合所有内容的整体比较器

       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; }; 

This Comparator is written lambda-style, for more info https://www.mkyong.com/java8/java-8-lambda-comparator-example/ . 此比较器以lambda样式编写,以获取更多信息https://www.mkyong.com/java8/java-8-lambda-comparator-example/

First it takes the ordered list of sortParams we made in step 1 and for each returns an array where position 0 has the value for jsonObj1 , and position 1 has the value for jsonObj2 and collects it in sortValuesList . 首先,它获取我们在步骤1中创建的sortParams的有序列表,并为每个数组返回一个数组,其中位置0具有jsonObj1的值,位置1具有jsonObj2的值并将其收集在sortValuesList Then for each attribute to sort on, it get the result of the individualComparator method. 然后,对于每个要排序的属性,它都会获得individualComparator方法的结果。 Then it goes down the line and returns as result of the overall comparison the first one that doesn't result in 0 (when a comparator results in 0 both values are equal). 然后,它沿线下降并作为整体比较的结果返回第一个不为0的值(当比较器的结果为0时,两个值相等)。

The only thing that's missing now is the asc/desc value from the request. 现在唯一缺少的是请求中的asc / desc值。 You can add that by chaining int comp1 = individualComparator(sortValuesList.get(0)[0], sortValuesList.get(0)[1]); 您可以通过链接int comp1 = individualComparator(sortValuesList.get(0)[0], sortValuesList.get(0)[1]); with a simple method that takes an int & a String and multiplies the int by -1 if the String equals "desc". 使用一个简单的方法,该方法需要一个int和一个String,如果String等于“ desc”,则将int乘以-1。 (Remember that in sortParams we added the value for order on position 1 of the array). (请记住,在sortParams我们在数组的位置1上添加了order的值)。

Because the first list we made, sortParams was ordered based on the priority indicated in the request, and we always did evertything in the order of this list, the result is a multi-sort in this order. 因为我们创建的第一个列表sortParams是根据请求中指示的优先级排序的,所以我们总是按照该列表的顺序进行所有操作,结果按此顺序进行了多排序。 It is generic & will be determined dynamically by the contents of returnParams in the request. 它是通用的,将由请求中returnParams的内容动态确定。 You can apply it to your list of JSONObjects by using Collections.sort() 您可以使用Collections.sort()将其应用于JSONObjects列表。

In your case a simple comparator that's provided with the sort parameters might be easier to understand than a bunch of nested comparators. 在您的情况下,与一堆嵌套的比较器相比,带有sort参数的简单比较器可能更易于理解。

Basically you'd do something like this: 基本上你会做这样的事情:

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;
   }
}

Note that this is just a stub to get you started. 请注意,这只是一个入门指南。 You'll need to add quite a few things: 您需要添加很多内容:

  • how to correctly use the parameters to extract the values from the json objects 如何正确使用参数从json对象提取值
  • how to convert the data based on the type 如何根据类型转换数据
  • how to handle nulls, missing or incompatible data (eg if a value should be sorted as "int" but it can't be parsed) 如何处理null,丢失或不兼容的数据(例如,如果值应按“ int”排序但无法解析)

Adding all those would be way too much for the scope of this question and depends on your data and requirements anyway. 添加所有这些对于这个问题的范围来说将是太多了,并且仍然取决于您的数据和要求。

My suggestion: learn about: 我的建议:了解:

  • Comparator.comparing which allows you to build your comparator by specifying the key extractor Comparator.comparing ,它允许您通过指定密钥提取器来构建比较器
  • Comparator.thanComparing which allows you to chain multiple comparators. Comparator.thanComparing允许您链接多个比较器。 The comparators later in the chain are called only if predecessors say the objects are equal 仅当前辈说对象相等时,才调用链中后面的比较器

A tutorial if you need one: https://www.baeldung.com/java-8-comparator-comparing 如果需要一个教程: https : //www.baeldung.com/java-8-comparator-comparing

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM