Home > Net >  Finding same date and counting the object using Stream API
Finding same date and counting the object using Stream API

Time:10-01

I need some help with the Stream API. I need to group a list of objects based on location and if location is same but date is different irrespective of time then increment count by 1. I have provided below code for class with getters setters and data and expected output.

public class Event {
    String location;
    String date;
    String count; 
 
    public String getCount() {
        return count;
    }

    public void setCount(String count) {
        this.count = count;
    }
    public String getLocation() {
        return location;
    }

    public String getDate() {
        return date;
    }

    public void setLocation(String location) {
        this.location = location;
    }

    public void setDate(String date) {
        this.date = date;
    }
} 

Data for Event Class:

Event event = new Event();
        
event.setLocation("98.55.62.162");
event.setDate("09/17/2022 12:05:43 PM");
event.add(event);

event.setLocation("98.55.62.162");
event.setDate("09/16/2022 12:05:45 PM");
event.add(event);

event.setLocation("98.55.62.162");
event.setDate("09/16/2022 12:05:47 PM");
event.add(event);

event.setLocation("98.55.62.162");
event.setDate("09/15/2022 12:05:49 PM");
event.add(event);

event.setLocation("98.55.62.163");
event.setDate("09/17/2022 12:05:31 PM");
event.add(event);

event.setLocation("98.55.62.163");
event.setDate("09/16/2022 12:05:22 PM");
event.add(event);

event.setLocation("98.55.62.163");
event.setDate("09/16/2022 12:05:11 PM");
event.add(event);

I tried to make Map<String, List<Event>>

Map<String, List<Event>> eventMap = events.stream().collect(Collectors.groupingBy(s -> s.getLocation()));

It's not working for me.
I need output like Map<String,Integer> map where String is location and count is based on Date. If date is different then increment count by 1. Time is not important here. I am not sure how to group them using stream API or for loop.

o/p: ["98.55.62.162":3 , "98.55.62.163":2]

CodePudding user response:

it is possible but kind of tricky. Check this

Event event = new Event();
        List<Event> events = new ArrayList<>();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/d/yyyy hh:mm:ss a");

        event.setLocation("98.55.62.162");
        event.setDate(LocalDate.parse("09/17/2022 12:05:43 PM", formatter));
        events.add(event);

        event = new Event();
        event.setLocation("98.55.62.162");
        event.setDate(LocalDate.parse("09/16/2022 12:05:45 PM", formatter));
        events.add(event);

        event = new Event();
        event.setLocation("98.55.62.162");
        event.setDate(LocalDate.parse("09/16/2022 12:05:47 PM", formatter));
        events.add(event);

        event = new Event();
        event.setLocation("98.55.62.162");
        event.setDate(LocalDate.parse("09/15/2022 12:05:49 PM", formatter));
        events.add(event);

        event = new Event();
        event.setLocation("98.55.62.163");
        event.setDate(LocalDate.parse("09/17/2022 12:05:31 PM", formatter));
        events.add(event);

        event = new Event();
        event.setLocation("98.55.62.163");
        event.setDate(LocalDate.parse("09/16/2022 12:05:22 PM", formatter));

        events.add(event);

        event = new Event();
        event.setLocation("98.55.62.163");
        event.setDate(LocalDate.parse("09/16/2022 12:05:11 PM", formatter));
        events.add(event);

        Map<String, Long> eventMap = events
                .stream()
                .collect(
                        Collectors.groupingBy(Event::getLocation)
                ).entrySet()
                .stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey, v -> v.getValue()
                                .stream()
                                .map(e -> e.getDate())
                                .distinct()
                                .count()));

CodePudding user response:

This answer is based on the answer by Demian. I have made the following changes:

  1. Use of Locale.ENGLISH with DateTimeFormatter which is not mandatory for your date-time values but is highly recommended because the date-time formatting/parsing API is Locale-sensitive.
  2. Keeping the type of Event#date as String while I recommend you use LocalDateTime and parse the string date-time string while constructing Event objects, the way Demian has done in his answer. If you have separate values for day, month, year, hour, minute etc., you can use one of LocalDateTime#of methods to populate Event#date.
  3. Use of .map(e -> LocalDateTime.parse(e.getDate(), formatter).toLocalDate()): In this mapping, I have first parsed Event#date into LocalDateTime and then extracted just the date part by using LocalDateTime#toLocalDate
    List<Event> events = new ArrayList<>();
    
    Event event = new Event();
    event.setLocation("98.55.62.162");
    event.setDate("09/17/2022 12:05:43 PM");
    events.add(event);
    
    ...
    ...
    ...
    
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss a", Locale.ENGLISH);
    
    Map<String, Long> eventMap = events
            .stream()
            .collect(
                    Collectors.groupingBy(Event::getLocation))
            .entrySet()
            .stream()
            .collect(Collectors.toMap(
                    Map.Entry::getKey, v -> v.getValue()
                            .stream()
                            .map(e -> LocalDateTime.parse(e.getDate(), formatter).toLocalDate())
                            .distinct()
                            .count()));
    
    System.out.println(eventMap);

Output:

{98.55.62.163=2, 98.55.62.162=3}

Learn more about the the modern date-time API from Trail: Date Time.

  • Related