流式遍历集合并使用优先级进行过滤

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

Stream through collection and filter using a priority

问题

我正尝试根据特定优先级对集合进行筛选。
该集合由具有特定类型字段的实体组成。

因此,我想要执行以下操作:
遍历/流式处理集合并且

  1. 查找第一个具有 getType = "type1" 的实体。如果此实体的价格 > 0,则返回此实体。否则,

  2. 查找下一个具有 getType = "type2" 的实体。如果此实体的价格 > 0,则返回此实体。否则,

  3. 查找下一个具有 getType = "type3" 的实体。如果此实体的价格 > 0,则返回此实体。

  4. 查找下一个具有 getType = "type4" 的实体。如果此实体的价格 > 0,则返回此实体。

  5. 查找下一个具有 getType = "type5" 的实体。如果此实体的价格 > 0,则返回此实体。

  6. 否则,返回。

我正尝试使用流来完成此操作,但筛选步骤是我无法创建此优先级筛选的地方。我还尝试过使用 for 循环遍历集合,但不确定如何实现先找到 type1 实体,然后依次是 type2 等等。

英文:

I am trying to filter a collection based on a certain priority.
The collection consists of entities with fields of a certain type.

So, I would like to do the following:
Iterate/Stream through the collection and

  1. Find the first entity which has a getType = "type1". If this entity has a price > 0 then return this entity. Otherwise

  2. Find the next entity which has a getType = "type2". If this has a price > 0 then return this. Otherwise

  3. Find the next entity which has a getType = "type3". If this has a price > 0 then return this.

  4. Find the next entity which has a getType = "type4". If this has a price > 0 then return this.

  5. Find the next entity which has a getType = "type5". If this has a price > 0 then return this.

  6. Otherwise return.

I am trying to do this using stream but the filter step is where I am unable to create this priority type of filtering. I also tried to loop through the collection using a for, however not sure how to implement findFirst entity of type1, then type2 etc..

答案1

得分: 1

如果您稍微重新阐述一下要求,将其转化为一系列流操作会更清晰 - 您应该返回具有最小“类型”的第一个满足 price > 0 的项目:

MyEntity result = myList.stream()
                        .filter(e -> e.getPrice() > 0)
                        .min(Comparator.comparing(MyEntity::getType))
                        .orElse(null);
英文:

If you rephrase the requirement a bit, it makes the translation to a series of operations of a stream much clearer - you should return the first item with price > 0 that has the minimal type:

MyEntity result = myList.stream()
                        .filter(e -> e.getPrice() > 0)
                        .min(Compartor.comparing(MyEntity::getType))
                        .orElse(null);

答案2

得分: 1

Original - Invalid reduction Approach

一种选择是颠倒操作顺序。例如:先筛选出 'price > 0' 的元素,然后在遇到元素时减少流。例如:

stream.filter(elem -> elem.price > 0)
    .reduce((elem1, elem2) -> elem1.type.compareTo(elem2.type) < 0 ? elem1 : elem2)

Edit - More correct reduction approach

一般来说,最好保持流操作是无状态的。因此,创建一个处理遍历列表并返回结果的函数可能是更正确的方法。使用流,可以定义一个自定义的 'reducer',它会跟踪先前检查的类型,以确定下一个结果是否可能是有效匹配。一旦找到有效匹配,它将始终被返回。

public static void main(String[] args) {
    List<Entity> data = Arrays.asList(eee(2, 6), eee(1, 0), eee(1, 10), eee(3, 7), eee(2, 0), eee(3, 5), eee(4, 0), eee(5, 0));
    System.out.println(data.stream().reduce(new Reducer()).filter(entity -&gt; entity != Reducer.NO_MATCH));
}

/*一旦找到匹配,始终使用它。对于给定类型,只会使用找到的第一个实体*/
public static final class Reducer implements BinaryOperator<Entity> {
    private int priorValidType;
    Reducer() { this.priorValidType = 0; }

    @Override
    public Entity apply(Entity result, Entity newElem) {
        int nextValidType = priorValidType + 1;
        if (priorValidType > 0 && result != NO_MATCH) return result; /*已经找到匹配,使用它*/
        if (result.type == nextValidType && result.price > 0) { priorValidType = nextValidType; return result; } /*result 是匹配*/
        if (newElem.type == nextValidType && newElem.price > 0) { priorValidType = nextValidType; return newElem; } /*newElem 是匹配*/
        if (result.type == nextValidType || newElem.type == nextValidType) { priorValidType = nextValidType; }
        return NO_MATCH; /*未找到匹配*/
    }

    public static final Entity NO_MATCH = new Entity(-1, -1);
}

public static final class Entity {
    private final int price, type;
    Entity(int type, int price) { this.price = price; this.type = type; }
    public String toString() { return "(" + type + ", " + price + ")"; }
    int getPrice() { return price; }
    int getType() { return type; }
    public static Entity eee(int type, int price) { return new Entity(type, price); }
}

Edit - Alternative approach using filter

可以创建一个类似于 reduce 方法的过滤器,调用 'findFirst' 时将具有短路的优势。在下面的代码中,第一个过滤器只允许给定类型(按顺序)的第一个遇到的元素通过。第二个过滤器确认其是否有效。

public static void main(String[] args) {
    List<Entity> data = Arrays.asList(eee(2, 6), eee(1, 0), eee(1, 10), eee(3, 7), eee(2, 0), eee(3, 5), eee(4, 0), eee(5, 0));
    System.out.println(data.stream().filter(new FirstTypeMatch()).filter(entity -&gt; entity.price > 0).findFirst());
}

/*仅允许第一个遇到的给定类型的元素通过*/
public static final class FirstTypeMatch implements Predicate<Entity> {
    private int priorValidType = 0;
    @Override
    public boolean test(Entity nextElem) {
        if (nextElem.type == (priorValidType + 1)) { priorValidType++; return true; }
        return false;
    }
}
英文:

Original - Invalid reduction Approach

One option is to flip the order of operations. Ex: filter where 'price > 0' and then reduce the stream as elements are encountered. Ex:

stream.filter(elem -&gt; elem.price &gt; 0)
.reduce((elem1, elem2) -&gt; elem1.type.compareTo(elem2.type) &lt; 0 ? elem1 : elem2)

Edit - More correct reduction approach

In general, it is best to keep stream operations stateless. Because of this creating a function which handles iterating through the list and returning the result may be a more correct approach. Using streams, a custom 'reducer' may be defined which keeps track of the prior type checked to determine if the next result is a possible valid match. Once a valid match is found, it is always returned.

    public static void main(String[] args)
{
List&lt;Entity&gt; data = Arrays.asList(eee(2, 6), eee(1, 0), eee(1, 10), eee(3, 7), eee(2, 0), eee(3, 5), eee(4, 0), eee(5, 0));
System.out.println(data.stream().reduce(new Reducer()).filter(entity -&gt; entity != Reducer.NO_MATCH));
}
/*Once a match is found, always use it. For a given type, only the first found entity of that type will be used*/
public static final class Reducer implements BinaryOperator&lt;Entity&gt;
{
private int priorValidType;
Reducer(){ this.priorValidType = 0; }
@Override
public Entity apply(Entity result, Entity newElem)
{
int nextValidType = priorValidType + 1;
if(priorValidType &gt; 0 &amp;&amp; result != NO_MATCH) return result; /*Match already found, use it*/
if(result.type == nextValidType &amp;&amp; result.price &gt; 0) { priorValidType = nextValidType; return result; } /*result is a match*/
if(newElem.type == nextValidType &amp;&amp; newElem.price &gt; 0) { priorValidType = nextValidType; return newElem; } /*newElem is a match*/
if(result.type == nextValidType || newElem.type == nextValidType) { priorValidType = nextValidType; }
return NO_MATCH; /*No match has been found*/
}
public static final Entity NO_MATCH = new Entity(-1, -1);
}
public static final class Entity
{
private final int price, type;
Entity(int type, int price){ this.price = price; this.type = type; }
public String toString(){ return &quot;(&quot; + type + &quot;, &quot; + price + &quot;)&quot;; }
int getPrice(){ return price; }
int getType(){ return type; }
public static Entity eee(int type, int price){ return new Entity(type, price); }
}

Edit - Alternative approach using filter

A filter may be created which does something similar to the reduce approach, and will have the benefit of short circuiting when calling 'findFirst'. In the below, the first filter only allows the first encounter with a given type (in order) to pass through. The second filter confirms it is valid.

    public static void main(String[] args)
{
List&lt;Entity&gt; data = Arrays.asList(eee(2, 6), eee(1, 0), eee(1, 10), eee(3, 7), eee(2, 0), eee(3, 5), eee(4, 0), eee(5, 0));
System.out.println(data.stream().filter(new FirstTypeMatch()).filter(entity -&gt; entity.price &gt; 0).findFirst());
}
/*Filter where the element is the first of the given type*/
public static final class FirstTypeMatch implements Predicate&lt;Entity&gt;
{
private int priorValidType = 0;
@Override
public boolean test(Entity nextElem)
{
if(nextElem.type == (priorValidType + 1)){ priorValidType++; return true; }
return false;
}
}

答案3

得分: 0

Based on a clarification by Andreas and a strict interpretation of the requirements as presently stated, this should work.

  • I basically streamed the types and then substreamed the list.
  • The inner stream is checked for each type in priority order.
  • if the first one found has a positive price, it is returned.
  • else the next type is checked, and the process repeated
  • In the event nothing matches, a special object is returned.

Class used to verify.

class MyClass {
    private int price;
    private String type;

    public MyClass(int price, String type) {
        this.price = price;
        this.type = type;
    }

    public int getPrice() {
        return price;
    }

    public String getType() {
        return type;
    }

    public String toString() {
        return String.format("[%s, %s]", price, type);
    }
}

Data

List<MyClass> list = List.of(new MyClass(0, "Type1"),
        new MyClass(10, "Type2"), new MyClass(2, "Type1"),
        new MyClass(2, "Type4"), new MyClass(2, "Type5"),
        new MyClass(2, "Type2"), new MyClass(10, "Type1"),
        new MyClass(1, "Type2"), new MyClass(2, "Type5"));

String[] types = {"Type1", "Type2", "Type3", "Type4", "Type5"};

Solution

MyClass selected = Arrays.stream(types)
        .map(type -> list.stream()
                .filter(obj -> obj.getType().equals(type))
                .findFirst()
                .orElse(null))
        .filter(a -> a != null && a.getPrice() > 0)
        .findFirst().orElse(new MyClass(-1, "Empty"));

System.out.println(selected);

Prints

[10, Type2]
英文:

Based on a clarification by Andreas and a strict interpretation of the requirements as presently stated, this should work.

  • I basically streamed the types and then substreamed the list.
  • The inner stream is checked for each type in priority order.
  • if the first one found has a positive price it is returned.
  • else the next type is checked and the process repeated
  • In the event nothing matches, a special object is returned.

Class used to verify.

class MyClass {
private int price;
private String type;
public MyClass(int price, String type) {
this.price = price;
this.type = type;
}
public int getPrice() {
return price;
}
public String getType() {
return type;
}
public String toString() {
return String.format(&quot;[%s, %s]&quot;, price, type);
}
}

Data

List&lt;MyClass&gt; list = List.of(new MyClass(0, &quot;Type1&quot;),
new MyClass(10, &quot;Type2&quot;), new MyClass(2, &quot;Type1&quot;),
new MyClass(2, &quot;Type4&quot;), new MyClass(2, &quot;Type5&quot;),
new MyClass(2, &quot;Type2&quot;), new MyClass(10, &quot;Type1&quot;),
new MyClass(1, &quot;Type2&quot;), new MyClass(2, &quot;Type5&quot;));
String[] types =
{ &quot;Type1&quot;, &quot;Type2&quot;, &quot;Type3&quot;, &quot;Type4&quot;, &quot;Type5&quot; };

Solution

MyClass selected = Arrays.stream(types)
.map(type -&gt; list.stream()
.filter(obj -&gt; obj.getType().equals(type))
.findFirst()
.orElse(null))
.filter(a -&gt; a != null &amp;&amp; a.getPrice() &gt; 0)
.findFirst().orElse(new MyClass(-1, &quot;Empty&quot;));
System.out.println(selected);

Prints

[10, Type2]
</details>
# 答案4
**得分**: 0
```java
我可以考虑使用流来首先根据类型(假设类型为小写)进行排序,然后使用过滤器来过滤掉负价格,然后使用findFirst来将其存储在Optional实体中。
Optional<Entity> entity = list.stream().sorted(Comparator.comparing(Entity::getType))
.filter(e -> e.price > 0).findFirst();
if (entity.isPresent()) {
System.out.println(entity.get().getType()); // 测试以验证
}
英文:

I can think of using stream to sort it first based on Type(assuming type is in lowercase) and then using filter to filter out the negative price and then used findFirst to store it in Optional Entity

Optional&lt;Entity&gt; entity = list.stream().sorted(Comparator.comparing(Entity::getType))
.filter( e -&gt; e.price &gt; 0 ).findFirst();
if(entity.isPresent()){
System.out.println(entity.get().getType()); // test to verify 
}

huangapple
  • 本文由 发表于 2020年9月18日 04:02:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/63945466.html
匿名

发表评论

匿名网友

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

确定