0

I want to get a list of hashmap that grouped by year and find the customer_id that doesn't contain a group based on a list of customer_id.

This is an example of the dataset.

List tagList = new ArrayList<>();

# Customer
HashMap<String, Object> customerMap = new HashMap<>();

## feeding data example
customerMap.put("date", "2018");
customerMap.put("name", "John");
customerMap.put("custemer_no", "1a");

tagList.add(customerMap);



customer_id_list = ['1a', '2b', '3c']
    
   customer_list = [
      {
        "date": "2019",
        "name": "John",
        "customer_id": "1a"
      },
      {
        "date": "2019",
        "name": "David",
        "customer_id": "2b"
      },
      {
        "date": "2020",
        "name": "John",
        "customer_id": "1a"
      },
      {
        "date": "2020",
        "name": "Alex",
        "customer_id": "3c"
      },
      {
        "date": "2021",
        "name": "John",
        "customer_id": "1a"
      }
    ]

This is a sample output that I want.

missing_customer_list = [
  {
    "date": "2019",
    "name": "Alex",
    "customer_id": "3c"
  },
  {
    "date": "2020",
    "name": "David",
    "customer_id": "2b"
  },
  {
    "date": "2021",
    "name": "David",
    "customer_id": "2b"
  },
  {
    "date": "2021",
    "name": "Alex",
    "customer_id": "3c"
  }
]

Do you have any ideas how I can get this sample output using stream comprehension?

If I cannot filter the list directly using stream comprehension, using a for loop is fine too.

I found the way how to group by year for now, but don't know how to handle the rest of the filtering..

Please help me! thanks in advance

List<Customer> result = customer_list.stream()
                .collect(Collectors.groupingBy(Customer::getDate))

UPDATE

Referring to the current answers,

I ve got stuck at the point where I couldn't create the new instance of customer.

So, Im planning to use for-loops to find the missing_customer_id during iteratation of customer_id_list and filtered_customer_list. Once I get the missing_customer_id in a list, I will try to re-create the Customers manually and add them into a new list to print

  • 2
    Question is not clear. You need to remove customers having same `customer_id` present as part of `customer_id_list` ? But your output has customers with same `customer_id` – Pramod Jul 05 '21 at 05:22
  • Possibly helpful: https://stackoverflow.com/a/63878934 – sorifiend Jul 05 '21 at 05:23
  • What i want is to filter the `customer_id` that doesn't include in the `customer_list` based on year for example, in 2018, data has two customer_id : 'a' and 'b'. so `filtered_customer_list` should have { "date": "2018", "customer_id": "c" }, since cusmter 'c' is not in the 2018 of `customer_list` – steamworks99 Jul 05 '21 at 05:29
  • Duplicate of: [*java stream sort/filter through list by properties*](https://stackoverflow.com/q/40223456/642706) – Basil Bourque Jul 05 '21 at 05:48

2 Answers2

1

There are many different ways to do this. These solutions can become inefficient for large lists. Because, calling .contains() on a List is a linear-time operation, meaning doing so n times is quadratic.

List<String> customerIdList = Arrays.asList("a", "b", "c");
List<Customer> customerList = new ArrayList<>();

customerList.add(new Customer("2018", "a"));
customerList.add(new Customer("2018", "b"));
customerList.add(new Customer("2019", "b"));
customerList.add(new Customer("2019", "c"));
customerList.add(new Customer("2020", "a"));
customerList.add(new Customer("2020", "c"));
customerList.add(new Customer("2021", "a"));

Map<String, List<Customer>> collect = customerList.stream()
            .collect(Collectors.groupingBy(Customer::getDate))
            .entrySet().stream().map(notExistCustomerByYear(customerIdList))
            .flatMap(Collection::stream)
            .collect(Collectors.groupingBy(Customer::getDate));

The method that accomplishes what I really want is as follows;

private Function<Map.Entry<String, List<Customer>>, List<Customer>> notExistCustomerByYear(List<String> customerIdList) {
    return e -> {
        List<String> customerIds = e.getValue().stream().map(Customer::getCustomerId).collect(Collectors.toList());
        List<String> notExistCustomerIdsInYear = customerIdList.stream().filter(id -> !customerIds.contains(id)).collect(Collectors.toList());
        return notExistCustomerIdsInYear.stream().map(id -> new Customer(e.getKey(), id)).collect(Collectors.toList());
    };
}
fatih
  • 1,285
  • 11
  • 27
  • Big thanks to you!! it really helped me to figure it out. I still need to dig into more based on your answers, but it is really useful!! Thank you again!! – steamworks99 Jul 06 '21 at 04:15
1

What you describe is not filtering. According to your question and comments, you want a completely new list with combinations of year and customer-id which were not present in the original list.

First, you can group customer-ids per year to have a better representation of your original list.

Map<Integer, List<String>> customerIdPerYear = customerList.stream()
       .collect(Collectors.groupingBy(
               Customer::getYear,
               Collectors.mapping(
                       Customer::getCustomerId,
                       Collectors.toList())));
System.out.println(customerIdPerYear);
// output: {2018=[a, b], 2019=[b, c], 2020=[a, c], 2021=[a]}

In a second step, you create a new list per year with the customer-ids not found in the original list. Finally, you can create new Customer objects and return a flattened list with flatMap.

List<Customer> missingCustomersPerYear = customerIdPerYear.entrySet().stream()
        .flatMap(e -> customerIdList.stream()
                .filter(id -> !e.getValue().contains(id))
                .map(id -> new Customer(e.getKey(), id)))
        .collect(Collectors.toList());
System.out.println(missingCustomersPerYear);
// output: [(2018, c), (2019, a), (2020, b), (2021, b), (2021, c)]

To be complete, here is the Customer class used for the above examples:

class Customer {

    private int year;
    private String customerId;

    public Customer(final int year, final String customerId) {
        this.year = year;
        this.customerId = customerId;
    }

    public int getYear() {
        return year;
    }

    public String getCustomerId() {
        return customerId;
    }

    @Override
    public String toString() {
        return "(" + year + ", " + customerId + ")";
    }
}
Matt
  • 12,848
  • 2
  • 31
  • 53
  • Big thanks to you!! it really helped me to figure it out. I still need to dig into more based on your answers, but it is really useful!! Thank you again!! – steamworks99 Jul 06 '21 at 04:16