【问题标题】:Split a List into chunks by an Element按元素将列表拆分为块
【发布时间】:2018-08-09 08:50:09
【问题描述】:

我有一个使用这个模型的对象(Pos)集合:

public class Pos {
    private String beforeChangement;
    private String type;
    private String afterChangement;
}

对象列表是这样的:

[
Pos(beforeChangement=Découvrez, type=VER, afterChangement=découvrir), 
Pos(beforeChangement=un, type=DET, afterChangement=un),
Pos(beforeChangement=large, type=ADJ, afterChangement=large), 
Pos(beforeChangement=., type=SENT, afterChangement=.), 
Pos(beforeChangement=Livraison, type=NOM, afterChangement=livraison), 
Pos(beforeChangement=et, type=KON, afterChangement=et), 
Pos(beforeChangement=retour, type=NOM, afterChangement=retour), 
Pos(beforeChangement=., type=SENT, afterChangement=.), 
Pos(beforeChangement=achetez, type=VER, afterChangement=acheter), 
Pos(beforeChangement=gratuitement, type=ADV, afterChangement=gratuitement), 
Pos(beforeChangement=., type=SENT, afterChangement=.), 
Pos(beforeChangement=allez, type=VER, afterChangement=aller), 
Pos(beforeChangement=faites, type=VER, afterChangement=faire), 
Pos(beforeChangement=vite, type=ADV, afterChangement=vite), 
Pos(beforeChangement=chers, type=ADJ, afterChangement=cher), 
Pos(beforeChangement=clients, type=NOM, afterChangement=client)]
Pos(beforeChangement=., type=SENT, afterChangement=.)
]

我想通过 beforeChangement 或 afterChangement == "." 的字段拆分此对象列表。具有这种格式(列表列表)List<List<SOP>>:

[

[Pos(beforeChangement=Découvrez, type=VER, afterChangement=découvrir), 
Pos(beforeChangement=un, type=DET, afterChangement=un),
Pos(beforeChangement=large, type=ADJ, afterChangement=large)],

[Pos(beforeChangement=Livraison, type=NOM, afterChangement=livraison), 
Pos(beforeChangement=et, type=KON, afterChangement=et), 
Pos(beforeChangement=retour, type=NOM, afterChangement=retour)],

[Pos(beforeChangement=achetez, type=VER, afterChangement=acheter), 
Pos(beforeChangement=gratuitement, type=ADV, afterChangement=gratuitement)],


[Pos(beforeChangement=allez, type=VER, afterChangement=aller), 
Pos(beforeChangement=faites, type=VER, afterChangement=faire), 
Pos(beforeChangement=vite, type=ADV, afterChangement=vite), 
Pos(beforeChangement=chers, type=ADJ, afterChangement=cher), 
Pos(beforeChangement=clients, type=NOM, afterChangement=client)]

]

就像执行一个反向 flatMap 以在被一个对象字段(即字符串“。”)分割后拥有一个数组或列表(块)列表。

您对如何使用 Streams 有任何想法吗?

谢谢大家

【问题讨论】:

    标签: java collections split java-8 java-stream


    【解决方案1】:

    嗯,我想用这样一个简单的循环来解决你的问题:

    List<List<Pos>> result = new ArrayList<>();
    List<Pos> part = new ArrayList<>();
    for(Pos pos : listPos){
        if(pos.getBeforeChangement().equals(".") || pos.getAfterChangement().equals(".")){
            result.add(part);//If the condition is correct then add the sub list to result list
            part = new ArrayList<>();// and reinitialize the sub-list
        } else {
            part.add(pos);// else just put the Pos object to the sub-list
        }
    }
    //Just in case the listPos not end with "." values then the last part should not be escaped
    if(!part.isEmpty()){
        result.add(part);
    }
    

    注意,问题不够清楚,您的 Object 类名为 SOP,而 Object 列表为 Pos,哪个是正确的,在我的回答中,我基于 public class Pos{..} 而不是 public class SOP{..}。


    【讨论】:

    • 谢谢你的回答我更正了Pos和SOP是一样的。
    • 此解决方案存在一个重大问题,即当Pos 的列表不 以. 结尾时,您的代码将跳过整个最后一句话。
    • 否@TomaszLinkowski 检查问题中的输出,具有. 的对象不包括在结果中。您也可以与我的回答中提到的问题的输出和演示的输出进行比较
    • 你是对的 - 根据问题 - 应该跳过包含 . 的对象(我没有注意到它,我的回答没有这样做)。但我的意思是,如果输入列表 not 以“点”对象结尾,那么您的代码只是 跳过 整个最后一个子列表,而不是包含这样的子列表或抛出错误。看到这个clone of your snippet,我从listPos 中删除了最后一行。请注意,输出中缺少"allez", "faites", "vite", "chers", "clients"。
    【解决方案2】:

    使用StreamEx 库,您可以使用groupRuns 方法将列表拆分为列表列表。

    例如:

    List<List<Pos>> collect = StreamEx.of(originalList.stream())
                .groupRuns((p1, p2) -> !(".".equals(p2.beforeChangement) || ".".equals(p2.afterChangement)))
                .collect(Collectors.toList());
    

    方法groupRuns 返回列表的Stream。在上面的示例中,它是第一个元素为. 的列表。

    您可以稍后过滤掉这些元素。例如使用map 方法:

    StreamEx.of(originalList.stream())
                .groupRuns((p1, p2) -> !(".".equals(p2.beforeChangement) || ".".equals(p2.afterChangement))) // returns Stream of lists with '.' element
                .map(l -> l.stream()
                            .filter(p -> !(".".equals(p.beforeChangement) || ".".equals(p.afterChangement))) //filter out element with '.'
                            .collect(Collectors.toList()))
                .filter(l -> !l.isEmpty()) // filter out empty lists
                .collect(Collectors.toList());
    

    【讨论】:

    • 不过,据我了解,这段代码会将点放在单独的列表中,对吧?
    • 如何排除“.”正在筹备中?
    • 而不是与点相关的map + filter(还有一个额外的collect),我会简单地使用以下内容:.filter(l -&gt; !isPeriod(l.get(0))) where boolean isPeriod(Pos pos) { return ".".equals(pos.beforeChangement) || ".".equals(pos.afterChangement); }
    • @TomaszLinkowski 你的例子是如果第一个元素是'.'-element 则过滤掉整个列表
    • 现在我更仔细地阅读了代码,我了解到句点没有放在单独的列表中。我很困惑,因为我认为groupRuns 中的谓词是(p1, p2) -&gt; !isPeriod(p1) &amp;&amp; !isPeriod(p2),而实际上它是(p1, p2) -&gt; !isPeriod(p2)。不过,这有点奇怪,因为这意味着句点位于列表的开头。但是,您说得对,我的过滤建议不起作用。相反,我会将谓词更改为(p1, p2) -&gt; !isPeriod(p1),然后如果匹配isPeriod,则使用peek 从每个列表中删除最后一个元素。
    【解决方案3】:

    好吧,我在这里会比较保守,我不会使用Streams(尽管有可能)。

    以下 sn-p 可以满足您的需要:

    List<Pos> posList;
    List<List<Pos>> result = new ArrayList<>();
    boolean startNewSentence = true;
    for (Pos pos : posList) {
        if (startNewSentence) {
            result.add(new ArrayList<>());
        }
        startNewSentence = isPeriod(pos);
        if (!startNewSentence) {
            result.get(result.size() - 1).add(pos);
        }
    }
    

    地点:

    boolean isPeriod(Pos pos) {
        return ".".equals(pos.beforeChangement()) || ".".equals(pos.afterChangement());
    }
    

    PS。请注意,英语中没有“变化”这样的词。动词“change”的名词也是“change”。

    【讨论】:

    • 谢谢托马斯。这个逻辑可以解决我的问题+1
    • @Dr.Mza 我更新了代码,结果中不包含带句点的元素。
    【解决方案4】:

    Collectors.groupingBy() 可以帮到你。

    【讨论】:

      【解决方案5】:

      假设您的列表对象名称是SOP 对象是listSOP。那么

      List<SOP> listSOP = new ArrayList<>();
      .... populate your list. 
      Map<String,List<SOP>> map = listSOP.stream().collect(Collectors.groupingBy(SOP::getBeforeChangement)
      

      这应该返回一个&lt;String(BeforeChangement), List&lt;SOP&gt;&gt; 类型的Map。

      这里getBeforeChangement 是SOP 类中的getter 方法,它应该返回变量beforeChangement 的值

      【讨论】:

      • 这不起作用:它将所有SOPs 与相同的beforeChangement 组合在一起,而不是将原始列表划分为有序的子列表。
      猜你喜欢
      • 2017-07-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-19
      • 1970-01-01
      • 2017-04-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多