0

I have a json Structure like this

[{
                "startAt": 1617605301292,
                "endAt": 1617605317095,
                "duration": 15803,
                "selection": {
                    "selected.Speed": "0",
                    "selected.Low": "65535",
                    "selected.Fast": "7173",
                    "selected.medium": "5"
                },
                "details": {
                    "phase": [{
                        "value": "2",
                        "timestamp": 1617605301316
                    }]
                }
            },....]

I need the count of every item inside selection object.

For Example

  • 0 occurred 4 times
  • 65535 occurred 2 times
  • 7173 occurred 3 times
  • 5 occurred 1 times

this is what I have done so far

Map<String, Long> counted = jObj.stream()
                        .filter(y -> ((Map<String, Object>) y.get("selection")).containsKey("selected.Speed"))
                        .map(x -> {
                            String Speed = String.valueOf(((Map<String, Object>) x.get("details"))
                                    .get("selected.Speed"));
                            return Speed;
                        }).collect(Collectors.toList()).stream()
                        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

so from this I am able to get the count of a single key but not the every key inside json Object. Pardon if this is a very basic question, I am new to the stream.

TIA

3
  • You are reading values from another entry than the one expected to hold the values x.get("details") which should be x.get("selection"). Is that the actual JSON or is there any intermediate processing step? And are you looking for values i.e. 3 times, 1 time... mapped to each property key? (i.e. 0 for selected.Speed occurred 4 times) Commented Apr 10, 2021 at 21:06
  • @tmarouane yes you are right I am looking for the same like 0 selected.Speed occurred 4 times 65535 selected.Low occurred 2 time etc actually based on that I need to prepare a json which will be like this [{ "selected.Low ":65535, "count":2 },{ "selected.Low ":0, "count":4 } ] Commented Apr 10, 2021 at 21:14
  • Makes more sense. I posted an answer. Commented Apr 10, 2021 at 22:06

2 Answers 2

2

Stream#mapMulti

If you're using , you can already make use of Stream#mapMulti for this use case. Here is how to solve it with this. If you're not using yet, scroll further to find the traditional solution to the problem

Map<String, Long> counted = jObj.stream()
        .map(x -> (Map<String, Object>) x.get("details"))
        .filter(x -> x.containsKey("selected.Speed"))
        .map(Map::values)
        .mapMulti((Collection<Object> collection, Consumer<String> consumer) -> {
            collection.forEach(element -> consumer.accept(String.valueOf(element)));
        })
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

With this solution, you don't have the overhead of an extra Stream creation


Traditional approach

Normally this should solve it for you using Stream#flatMap, Map#values then List#stream and mapping every entry to a String

Map<String, Long> counted = jObj.stream()
                        .filter(y -> ((Map<String, Object>) y.get("details")).containsKey("selected.Speed"))
                        .flatMap(x -> ((Map<String, Object>)x.get("details"))
                                                          .values()
                                                          .stream()
                                                          .map(String::valueOf))
                        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

You can even use an extra map intermediate operation to avoid casting x.get("details") to a Map<String, Object> twice

Map<String, Long> counted = jObj.stream()
                        .map(x -> (Map<String, Object>) x.get("details"))
                        .filter(x -> x.containsKey("selected.Speed"))
                        .map(Map::values)
                        .flatMap(Collection::stream)
                        .map(String::valueOf)
                        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

Here is the full test I've written for this

List<Map<String, Object>> jObj = new ArrayList<>();
jObj.add(Map.of("details", Map.of("selected.Speed", "0", "selected.Low", "65535", "selected.Fast", "8000", "selected.medium", "5")));
jObj.add(Map.of("details", Map.of("selected.Speed", "1", "selected.Low", "32", "selected.Fast", "8000", "selected.medium", "5")));
jObj.add(Map.of("details", Map.of("selected.Speed", "2", "selected.Low", "32", "selected.Fast", "8000", "selected.medium", "5")));
jObj.add(Map.of("details", Map.of("selected.Speed", "0", "selected.Low", "65535", "selected.Fast", "8000", "selected.medium", "5")));


Map<String, Long> counted = jObj.stream()
        .map(x -> (Map<String, Object>) x.get("details"))
        .filter(x -> x.containsKey("selected.Speed"))
        .map(Map::values)
        .flatMap(Collection::stream)
        .map(String::valueOf)
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

System.out.println("counted = " + counted);

Which prints the expected result

counted = {0=2, 1=1, 2=1, 8000=4, 5=4, 65535=2, 32=2}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, is there any way I can get key as well with the output. actually based on that I need to prepare a json which will be like this [{ "selected.Low ":65535, "count":2 },{ "selected.Low ":0, "count":4 } ]
Ok I guess this might be a second question @DoctorWho because the solution will differ quite much. Can I ask only one question per post ?
1

Here is how you should iterate through your JSON entries and process its fields:

jObj.stream()
    .map(x -> (Map<String, Object>) x.get("selection"))
    .filter(x -> x.containsKey("selected.Speed"))
    .flatMap(values -> values.entrySet().stream()) // flat-mapping entries to group them 
    .collect(Collectors.groupingBy(Map.Entry::getKey)) // group entries by their keys: `selected.Low`, `selected.Fast`...
    .entrySet()
    .stream() // streaming through grouped entries
    .map(e -> new AbstractMap.SimpleEntry<>(
            e.getKey(),
            e.getValue().stream().map(Map.Entry::getValue).collect(Collectors.groupingBy(Function.identity(), Collectors.counting())))
    ) // grouping entries values counts
    .collect(Collectors.toList());

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.