2

I have been trying to sort a JSONArray using Java 8's Stream api. However I guess I couldn't get the type casting done right.

The far I could get is the following piece of code.,

String objects = "[\n" +
                    "  {\n" +
                    "    \"lat\": \"-16.408545\",\n" +
                    "    \"lon\": \"-71.539105\",\n" +
                    "    \"type\": \"0\",\n" +
                    "    \"distance\": \"0.54\"\n" +
                    "  },\n" +
                    "  {\n" +
                    "    \"lat\": \"-16.4244317845\",\n" +
                    "    \"lon\": \"-71.52562186\",\n" +
                    "    \"type\": \"1\",\n" +
                    "    \"distance\": \"1.87\"\n" +
                    "  },\n" +
                    "  {\n" +
                    "    \"lat\": \"-16.4244317845\",\n" +
                    "    \"lon\": \"-71.52562186\",\n" +
                    "    \"type\": \"1\",\n" +
                    "    \"distance\": \"0.22\"\n" +
                    "  }\n" +
                    "  {\n" +
                    "    \"lat\": \"-16.4244317845\",\n" +
                    "    \"lon\": \"-71.52562186\",\n" +
                    "    \"type\": \"1\",\n" +
                    "    \"distance\": \"2.69\"\n" +
                    "  }\n" +
                    "]";
            JSONArray objectsArray = (JSONArray) new JSONParser().parse(objects);

            objectsArray.stream().sorted(Comparator.comparing(a -> ((JSONObject) a).get("distance")));

I get the following error.

Error:(42, 62) java: incompatible types: inferred type does not conform to upper bound(s)
        inferred: java.lang.Object
        upper bound(s): java.lang.Comparable<? super java.lang.Object>

I also referred this question. But I feel that this could be done in an easier way with streams. Any Ideas? I use the following dependency for JSONObject

    <dependency>
        <groupId>com.googlecode.json-simple</groupId>
        <artifactId>json-simple</artifactId>
        <version>1.1</version>
    </dependency>
Stefan Zobel
  • 3,182
  • 7
  • 28
  • 38
Prachi Sharma
  • 71
  • 1
  • 6
  • Which `JSONArray` class are you using? For example, I found `javax.json.JSONArray` that extends `List`, not `List`, which your code seems to expect. – Eran Dec 21 '17 at 12:07

2 Answers2

5

Probably you are looking for code like this:

objectsArray.stream().sorted(
    Comparator.comparing(a ->  Double.valueOf((String)((JSONObject) a).get("distance")))
).forEach(o -> System.out.println(o));

The casts that we are using are the following:

((JSONObject) a).get("distance")) //to get the distance as an Object

and then

((String) ((JSONObject) a).get("distance"))) // this Object is actually a String, so cast to it

and then

Double.valueOf((String)((JSONObject) a).get("distance")) //this String, however, is a double, and we want to compare the double size, not alphabetically 

Cause you'd need to compare based on the double value of the distance.

However, there are multiple things to improve. As we are using OOP here, it probably makes sense to at least convert the "String" stream to some objects that'd make sense.

EDIT:

Here's how you probably should have done it:

//define a class to conform with OOP stuff
public static class Coordinate {

    private final Double lat;
    private final Double lon;
    private final Integer type;
    private final Double distance;

    public Coordinate(Double lat, Double lon, Integer type, Double distance){
        this.lat = lat;
        this.lon = lon;
        this.type = type;
        this.distance = distance;
    }

    public Double getDistance() {
        return distance;
    }
}

// define a conversion logic
private static Coordinate convert(JSONObject raw){
    Double distance = Double.valueOf((String) raw.get("distance"));
    Double lon = Double.valueOf((String) raw.get("lon"));
    Double lat = Double.valueOf((String) raw.get("lat"));
    Integer type = Integer.valueOf((String) raw.get("type"));
    return new Coordinate(lat, lon, type, distance);
}

then your method is as easy as writing:

List<JSONObject> coordinates = (List<JSONObject>)new JSONParser().parse(objects);
List<Coordinate> sortedCoordinates = coordinates
                .stream()
                .map(raw -> convert(raw))
                .sorted(Comparator.comparing(Coordinate::getDistance))
                .collect(Collectors.toList());

Another tip I have for you is using a better library like https://github.com/FasterXML/jackson for your object mapping.

Nikola Yovchev
  • 9,498
  • 4
  • 46
  • 72
2

Try this.

JSONArray objectsArray = (JSONArray) new JSONParser().parse(OBJECTS);
List<JSONObject> jo = (List<JSONObject>) objectsArray.parallelStream()
                .sorted(Comparator.comparing((t) -> Double.parseDouble(((JSONObject) t).get("distance").toString())))
                .collect(Collectors.toList());

System.out.println(JSONArray.toJSONString(jo));
Sachith Dickwella
  • 1,388
  • 2
  • 14
  • 22