4

Here's what I have so far:

Map<Care, List<Correlative>> mapOf = quickSearchList
                .stream()
                .map(QuickSearch::getFacility)
                .collect(Collectors.flatMapping(facility -> facility.getFacilityCares().stream(),
                    Collectors.groupingBy(FacilityCare::getCare,
                        Collectors.mapping(c -> {
                            final Facility facility = new Facility();
                            facility.setId(c.getFacilityId());
                            return Correlative.createFromFacility(facility);
                        }, Collectors.toList()))));

I have a list of Quick Searches to begin with. Each item in the quick search has a single facility as in:

public class QuickSearch {
  Facility facility;
}

In every Facility, there's a List of FacilityCare as in:

public class Facility {
  List<FacilityCare> facilityCares;
}

And finally, FacilityCare has Care property as in:

public class FacilityCare {
   Care care;
}

Now, the idea is to convert a List of QuickSearch to a Map of <Care, List<Correlative>>. The code within the mapping() function is bogus, in the example above. FacilityCare only has facilityID and not Facility entity. I want the facility object that went as param in flatMapping to be my param again in mapping() function as in:

Collectors.mapping(c -> Correlative.createFromFacility(facility)) 

where "facility" is the same object as the one in flatMapping.

Is there any way to achieve this? Please let me know if things need to be explained further.

Edit: Here's a solution doesn't fully utilize Collectors.

final Map<Care, List<Correlative>> mapToHydrate = new HashMap<>();

 quickSearchList
  .stream()
  .map(QuickSearch::getFacility)
  .forEach(facility -> {
    facility.getFacilityCares()
      .stream()
      .map(FacilityCare::getCare)
      .distinct()
      .forEach(care -> {
        mapToHydrate.computeIfAbsent(care, care -> new ArrayList<>());
        mapToHydrate.computeIfPresent(care, (c, list) -> {
          list.add(Correlative.createFromFacility(facility));
          return list;
        });
      });
    });
      
Anjil Dhamala
  • 1,544
  • 3
  • 18
  • 37
  • Questions like this could be answered more easily (or quickly) answered if you would at least include the required getters and setters from each class. – WJS Oct 13 '20 at 00:12
  • @WJS: In this instance, there's no extra logic to the getters and setters other than what they are. Please let me know which part I could clarify on further; – Anjil Dhamala Oct 13 '20 at 00:54
  • One would be `map(QuickSearch::getFacility)`. Does that return the facilities list? If so, then it would probably be streamed if you wanted to process each facility in each list of all the `QuickSearch` objects in the `quickSearchList` – WJS Oct 13 '20 at 01:31
  • @WJS My apologies. That was meant to be a One to One relation between QuickSearch and Facility. I have updated my question. Thanks for pointing it out. – Anjil Dhamala Oct 13 '20 at 01:55
  • OK. I updated my answer. It compiles but since I don't have test data I don't know if it does what you want. – WJS Oct 13 '20 at 02:02

3 Answers3

2

This is what I came up with based on the information provided. The Facility and Care are stored in a temp array to be processed later in the desired map.

Map<Care, List<Correlative>> mapOf = quickSearchList.stream()
        .map(QuickSearch::getFacility)
        .flatMap(facility -> facility
                .getFacilityCares().stream()
        .map(facCare->new Object[]{facility, facCare.getCare()}))
        .collect(Collectors.groupingBy(obj->(Care)obj[1], Collectors
                .mapping(obj -> Correlative.createFromFacility(
                        (Facility)obj[0]),
                        Collectors.toList())));

I prepared some simple test data and this seems to work assuming I understand the ultimate goal. For each type of care offered, it puts all the facilities that offer that care in an associated list of facilities.

WJS
  • 36,363
  • 4
  • 24
  • 39
  • Thank you for taking your time to reply. The piece of logic within your mapping is not applicable and that's the central piece I'm struggling with. The facility object we get when we first start the collect() method is the one I need in the final List. I hope that makes more sense. – Anjil Dhamala Oct 13 '20 at 02:14
  • 1
    So how does the ID come into play? Are you doing any filtering to see which one to add to the Correlative list . Also, something else you could do is edit your question and provide a non-stream solution with loops using the exact methods required that yields the correct map. If I can't help, that might help others help you. – WJS Oct 13 '20 at 02:25
  • It doesn't. I just wanted to show how I got the code to compile into the correct return type and thereby correct type for the mapOf variable. However, posting a non-stream solution is an excellent recommendation. I'll do that. – Anjil Dhamala Oct 13 '20 at 02:28
  • This is an excellent workaround for the time being. Thank you! Edit: Just realized you have facility object as first element in Object array. It's not available in the .map() scope. – Anjil Dhamala Oct 13 '20 at 18:54
2

Sometimes, streams are not the best solution. This seems to be the case, because you are losing each facility instance when going down the pipeline.

Instead, you could do it as follows:

Map<Care, List<Correlative>> mapToHydrate = new LinkedHashMap<>();
quickSearchList.forEach(q -> {
    Facility facility = q.getFacility();
    facility.getFacilityCares().forEach(fCare -> 
        mapToHydrate.computeIfAbsent(fCare.getCare(), k -> new ArrayList<>())
                    .add(Correlative.createFromFacility(facility)));
});

This uses the return value of Map.computeIfAbsent (which is either the newly created list of correlatives or the already present one).

It is not clear from your question why you need distinct cares before adding them to the map.


EDIT: Starting from Java 16, you might want to use Stream.mapMulti:

Map<Care, List<Correlative>> mapToHydrate = quickSearchList.stream()
    .map(QuickSearch::getFacility)
    .mapMulti((facility, consumer) -> facility.getFacilityCares()
        .forEach(fCare -> consumer.accept(Map.entry(fCare.getCare(), facility))))
    .collect(Collectors.groupingBy(
        e -> e.getKey(), 
        Collectors.mapping(
            e -> Correlative.createFromFacility(e.getValue()),
            Collectors.toList())));
fps
  • 33,623
  • 8
  • 55
  • 110
  • 1
    Based on the classes provided, `facility.getFacilityCares` returns a list of `FacilityCare`. So you need to call a `getCare` method for each FacilityCare. But once you make that change, your result is the same as mine. And your method is much cleaner and more to the point. – WJS Oct 13 '20 at 15:07
  • 1
    @fps: mapMulti is exactly what I am looking for. Even though we can't/don't use Java 16, knowing this solution puts my mind at ease. Thanks a lot! – Anjil Dhamala Oct 13 '20 at 18:52
1

Inspired by @fps answer, I was able to come up with a solution that will work for the time being (pre-Java16).

Map<Care, List<Correlative>> mapOf = quickSearchList
            .stream()
            .map(QuickSearch::getFacility)
            .map(expandIterable())
            .collect(
                Collectors.flatMapping(map -> map.entrySet().stream(),
                    Collectors.groupingBy(Map.Entry::getKey,
                        Collectors.mapping(entry -> Correlative.createFromFacility(entry.getValue()),
                            Collectors.toList()
                        )
                    )
                ));
    }

    public Function<Facility, Map<Care, Facility>> expandIterable() {
        return facility -> facility.getFacilityCares()
            .stream()
            .map(FacilityCare::getCare)
            .distinct()
            .collect(Collectors.toMap(c -> c, c -> facility));
    }

Basically, I added a method call that returns a Function that takes in Facility as argument and returns a Map of Care as key with Facility as value. That map is used in the collection of the previous stream.

Anjil Dhamala
  • 1,544
  • 3
  • 18
  • 37