2
    class Employee{
      private Integer employeeId;
      private Integer projectId;
      private String mobileNumbers;
    }

I want to convert List of employees to Map of projectId & mobile numbers. Mobile numbers can have multiple values comma separated(eg: mobileNumbers="7364567347,5784345445")

I want to split mobile number with partition of 100. Hence the value datatype is Set<Set>

Two things which I am not able to achieve in Java 8 using stream 1.Not able to split multiple mobileNumber as individual element in Set object. 2.Not able to partition

Eg:

  Employee[employeeId=1,projectId=1,mobileNumbers="1111,2222"]
  Employee[employeeId=2,projectId=1,mobileNumbers="33333"]
  Employee[employeeId=3,projectId=2,mobileNumbers="44444,5555"]

Expected : {1=[["1111","2222","33333"]], 2=[["44444","5555"]]}

I am able to proceed till Map<ProjectId,Set>. I am not able to split mobileNumbers

Map<Integer,Set<String>> result2=employeeList.stream()
         .collect(
          Collectors.groupingBy(Employee::getProjectId,
              Collectors.mapping(Employee::getMobileNumbers, Collectors.toSet()))
      );

Two things which I am not able to achieve in Java 8 using stream 1.Not able to split multiple mobileNumber as individual element in Set object. 2.Not able to partition `

KFH
  • 21
  • 1
  • The groupBy looks pretty much correct. All you need to do now is split the phone numbers (try using `String.split`) and then combining the collection of arrays into one set (probably using some kind of `flatMap`) – Jorn Jun 13 '23 at 11:31

4 Answers4

1

As always with this type of question, the imperative approach is much easier to read and reason about:

        Map<Integer, Set<String>> result = new HashMap<>();
        for (Employee employee : employeeList) {
            Set<String> set = result.result.computeIfAbsent(employee.getProjectId(), id -> new HashSet<>());
            set.addAll(Arrays.asList(employee.getMobileNumbers().split(",")));
        }

Once you understand this approach, you may even be able to turn it into a functional pipeline. But if you want this code to be maintainable, you probably shouldn't.

Jorn
  • 20,612
  • 18
  • 79
  • 126
0

To use streams only, you need the flatMapping method, which was not added until Java 9. This answer has a starting point, and then you need code like this:

Map<Integer,Set<String>> result2 = employeeList.stream()
    .collect(Collectors.groupingBy(
        Employee::getProjectId,
        flatMapping(
            e -> Arrays.stream(e.getMobileNumbers().split(",")),
            Collectors.toSet())));
Brett Kail
  • 33,593
  • 2
  • 85
  • 90
0

You need to split the grouped values and flatmap into Set in the downstream collector:

Map<Integer, Set<String>> result2 = employeeList.stream()
    .collect(Collectors.groupingBy(Employee::getProjectId,
        Collectors.mapping(Employee::getMobileNumbers,
            Collectors.flatMapping(s -> Arrays.stream(s.split(",")), 
                Collectors.toSet()))));
{1=[33333, 1111, 2222], 2=[44444, 5555]}
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
0

If I understand correctly, you want to have a Map<Integer, Set<Set<String>>>, and not a Map<Integer, Set<String>> as in the previous solutions. In my opinion, making the partitions using Streams makes the code illegible. Thus, I'd resort to an additional function to check the set conditions. To construct the example, I chose three as the maximum number of elements in a set, so you must adjust the answer according to your needs.

   final List<Employee> employees = Arrays.asList(
                new Employee(1, 1, "1111,2222"),
                new Employee(2, 1, "33333,4444"),
                new Employee(3, 2, "44444,5555")
        );

        final Map<Integer, Set<Set<String>>> idToNumberPartitions = employees.stream()
                .collect(Collectors.groupingBy(
                        Employee::getProjectId,
                        Collector.of(
                                HashSet::new,
                                (set, employee) -> addNumbersToPartition(set, employee.getMobileNumbers().split(",")),
                                (set1, set2) -> {
                                    set1.addAll(set2);
                                    return set1;
                                }
                        )
                ));


    }

    private static void addNumbersToPartition(final Set<Set<String>> allSets, final String[] numbers) {
        Set<String> currentSet = null;
        for (final String number : numbers) {
            if (currentSet == null || currentSet.size() >= 3) {
                currentSet = new HashSet<>();
                allSets.add(currentSet);
            }
            currentSet.add(number);
        }
    }
}

For the example above, this prints:

{1=[[1111, 2222], [33333, 4444]], 2=[[44444, 5555]]}

Consult the docs to find out more about the custom Collector:

    public static<T, R> Collector<T, R, R> of(Supplier<R> supplier,
                                              BiConsumer<R, T> accumulator,
                                              BinaryOperator<R> combiner,
                                              Characteristics... characteristics)
SkogensKonung
  • 601
  • 1
  • 9
  • 22