Java 8 Lambdas flatmapping, groupingBy and mapping to get a Map of T and List<K>

huangapple go评论74阅读模式
英文:

Java 8 Lambdas flatmapping, groupingBy and mapping to get a Map of T and List<K>

问题

这是我目前所拥有的:

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()))));

我有一个快速搜索的列表作为起点。快速搜索中的每个项目都有一个单独的设施,如下所示:

public class QuickSearch {
    Facility facility;
}

在每个设施中,都有一个 FacilityCare 的列表,如下所示:

public class Facility {
    List<FacilityCare> facilityCares;
}

最后,FacilityCare 有一个 Care 属性,如下所示:

public class FacilityCare {
    Care care;
}

现在,想法是将 QuickSearch 的列表转换为 Map<Care, List<Correlative>>

在 mapping() 函数内部的代码是虚构的,如上面的示例所示。FacilityCare 只有 facilityID,没有 Facility 实体。我希望 flatMapping 中作为参数传递的 facility 对象再次成为 mapping() 函数中的参数,如下所示:

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

其中 "facility" 是与 flatMapping 中的那个对象相同的对象。

是否有办法实现这一点?如果需要进一步解释,请告诉我。

编辑:
下面是一个没有充分利用 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, key -> new ArrayList<>());
                mapToHydrate.computeIfPresent(care, (c, list) -> {
                    list.add(Correlative.createFromFacility(facility));
                    return list;
                });
            });
    });
英文:

Here's what I have so far:

Map&lt;Care, List&lt;Correlative&gt;&gt; mapOf = quickSearchList
    			.stream()
    			.map(QuickSearch::getFacility)
    			.collect(Collectors.flatMapping(facility -&gt; facility.getFacilityCares().stream(),
    				Collectors.groupingBy(FacilityCare::getCare,
    					Collectors.mapping(c -&gt; {
    						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&lt;FacilityCare&gt; 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 &lt;Care, List&lt;Correlative&gt;&gt;.
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 -&gt; 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&lt;Care, List&lt;Correlative&gt;&gt; mapToHydrate = new HashMap&lt;&gt;();

 quickSearchList
  .stream()
  .map(QuickSearch::getFacility)
  .forEach(facility -&gt; {
    facility.getFacilityCares()
      .stream()
      .map(FacilityCare::getCare)
      .distinct()
      .forEach(care -&gt; {
        mapToHydrate.computeIfAbsent(care, care -&gt; new ArrayList&lt;&gt;());
    	mapToHydrate.computeIfPresent(care, (c, list) -&gt; {
    	  list.add(Correlative.createFromFacility(facility));
    	  return list;
    	});
      });
    });

答案1

得分: 2

这是根据提供的信息我得出的结果。FacilityCare 存储在一个临时数组中,以便稍后在所需的映射中进行处理。

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())));

我准备了一些简单的测试数据,这个代码似乎在我理解的最终目标的前提下可以工作。对于每种提供的 care,它将所有提供该 care 的设施放入一个关联的 facilities 列表中。

英文:

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&lt;Care, List&lt;Correlative&gt;&gt; mapOf = quickSearchList.stream()
        .map(QuickSearch::getFacility)
        .flatMap(facility -&gt; facility
                .getFacilityCares().stream()
        .map(facCare-&gt;new Object[]{facility, facCare.getCare()}))
        .collect(Collectors.groupingBy(obj-&gt;(Care)obj[1], Collectors
                .mapping(obj -&gt; 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.

答案2

得分: 2

有时候,流并不是最佳的解决方案。这似乎是情况,因为当沿着管道走下去时,您会丢失每个 facility 实例。

相反,您可以按以下方式操作:

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)));
});

这里使用了 Map.computeIfAbsent 的返回值(可以是新创建的相关列表,也可以是已存在的)。

从您的问题中不清楚在将它们添加到映射之前为什么需要不同的 cares。

编辑: 从 Java 16 开始,您可能会想使用 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())));
英文:

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&lt;Care, List&lt;Correlative&gt;&gt; mapToHydrate = new LinkedHashMap&lt;&gt;();
quickSearchList.forEach(q -&gt; {
    Facility facility = q.getFacility();
    facility.getFacilityCares().forEach(fCare -&gt; 
        mapToHydrate.computeIfAbsent(fCare.getCare(), k -&gt; new ArrayList&lt;&gt;())
                    .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&lt;Care, List&lt;Correlative&gt;&gt; mapToHydrate = quickSearchList.stream()
    .map(QuickSearch::getFacility)
    .mapMulti((facility, consumer) -&gt; facility.getFacilityCares()
        .forEach(fCare -&gt; consumer.accept(Map.entry(fCare.getCare(), facility))))
    .collect(Collectors.groupingBy(
        e -&gt; e.getKey(), 
        Collectors.mapping(
            e -&gt; Correlative.createFromFacility(e.getValue()),
            Collectors.toList())));

答案3

得分: 1

受 @fps 回答的启发,我能够提出一个解决方案,暂时适用于(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));
}

基本上,我添加了一个方法调用,它返回一个以 Facility 为参数的函数,并返回一个以 Care 为键、以 Facility 为值的 Map。该 Map 在前面流的收集中被使用。

英文:

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

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

	public Function&lt;Facility, Map&lt;Care, Facility&gt;&gt; expandIterable() {
		return facility -&gt; facility.getFacilityCares()
			.stream()
			.map(FacilityCare::getCare)
			.distinct()
			.collect(Collectors.toMap(c -&gt; c, c -&gt; 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.

huangapple
  • 本文由 发表于 2020年10月13日 06:52:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/64326329.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定