Eu tenho uma lista de resultado. Eu preciso encontrar os resultados passados contar. Mas há uma relação entre alguns itens na lista. Por exemplo. Tenho lista como
1.0 - false
2.0 - true
3.0 - false
4.0 - true
1.1 - true
3.1 - true
Em seguida, a contagem passou deve ser 2 e não a 4. Porque eu quero grupo da lista com base no id (1,1.2,1.3,1.xx no grupo single) e marcá-lo como passagem se todos os artigos do grupo é passar. Tenho grupo tentou usar groupingBy
e eu tenho um mapa do meu comportamento esperado. I pode iterar o mapa e obter a contagem. Mas eu quero saber se existe alguma maneira eu posso fazer isso simplesmente usando Java 8.
public class Main {
static class Resultx {
double id = 1;
Boolean passed = false;
public void setId(double id) {
this.id = id;
}
public double getId() {
return id;
}
public void setAsPassed() {
this.passed = true;
}
public Boolean getPassed() {
return passed;
}
@Override
public String toString() {
return getId() + " - " + getPassed();
}
}
public static void main(String[] args) {
List<Resultx> results = new ArrayList<>();
for (int i = 1; i < 5; i++) {
Resultx result = new Resultx();
result.setId(i);
if (i % 2 == 0) {
result.setAsPassed();
}
results.add(result);
}
for (int i = 1; i < 5; i += 2) {
Resultx result = new Resultx();
result.setId(i + .1);
result.setAsPassed();
results.add(result);
}
System.out.println(results.size());
results.forEach(System.out::println);
System.out.println(results.stream().filter(Resultx::getPassed).count());
System.out.println(results.stream().filter(e -> !e.getPassed()).count());
System.out.println(results.stream().collect(Collectors.groupingBy(r -> (int) (r.getId()))));
}
}
Resultado
Total count - 6
1.0 - false
2.0 - true
3.0 - false
4.0 - true
1.1 - true
3.1 - true
Total pass count - 4
Total fail count - 2
{1=[1.0 - false, 1.1 - true], 2=[2.0 - true], 3=[3.0 - false, 3.1 - true], 4=[4.0 - true]}
Quero contagem geral passagem e global falhar contagem. que é 2 e dois
Há algumas coisas estranhas sobre seu código, como o uso de um double
ID, que você acabou de lançar a int
sua operação de agrupamento ou usando um Boolean
para você passed
propriedade em vez de apenas boolean
. Usando o tipo de referência Boolean
, abre a oportunidade de ser null
, o que você teria que pega, se esta possibilidade se destina. Caso contrário, basta utilizar boolean
.
Também não está claro, o que resultará realmente querem. O exemplo não é suficiente para descrever.
Se você só quer contar os grupos, onde true
significa “todos onde a verdadeira” e false
significa “em algum lugar false
”, você pode fazê-lo tão simples quanto
Map<Boolean,Long> counts = results.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(r -> (int)r.getId(), Resultx::getPassed, Boolean::logicalAnd),
m -> m.values().stream()
.collect(Collectors.partitioningBy(b -> b, Collectors.counting()))
));
Se você quiser contar os elementos dos grupos, torna-se mais complicado.
int totalPassedCount = results.stream()
.collect(Collectors.collectingAndThen(Collectors.groupingBy(r -> (int)r.getId(),
Collector.of(() -> new Object() { int count = 0; boolean pass = true; },
(o, r) -> { o.count++; o.pass &= r.getPassed(); },
(x, y) -> { x.count += y.count; x.pass &= y.pass; return x; },
o -> o.pass? o.count: 0
)),
(Map<Integer,Integer> x) -> x.values().stream().mapToInt(i -> i).sum()
));
System.out.println(totalPassedCount);
Este utiliza um coletor de costume como a jusante coletor para groupingBy
. Esta coleta coletor costume em um objeto mantendo a contagem de elementos e se todos os elementos passaram, então, em uma etapa de acabamento, ele substitui estes objeto com a contagem, se todos os elementos do grupo passou ou zero se não. Em seguida, uma etapa de acabamento é adicionado ao groupingBy
coletor que irá resumir todos esses valores.
solução acima é para obter as contagens passadas, como você pediu no início da sua pergunta. Desde que você está pedindo para os dois no final, você poderia usar
Map<Boolean,Integer> counts = results.stream()
.collect(Collectors.collectingAndThen(Collectors.groupingBy(r -> (int)r.getId(),
Collector.of(() -> new Object() { int count = 0; boolean pass = true; },
(o, r) -> { o.count++; o.pass &= r.getPassed(); },
(x, y) -> { x.count += y.count; x.pass &= y.pass; return x; }
)),
m -> m.values().stream().collect(
Collectors.partitioningBy(o -> o.pass, Collectors.summingInt(o -> o.count)))
));
para obter ambos.
Ou, como é complicado para obter a regra destina-se a partir de um único exemplo,
Map<Boolean,Integer> counts = results.stream()
.collect(Collectors.collectingAndThen(Collectors.groupingBy(r -> (int)r.getId(),
Collector.of(() -> new Object() { int passed, failed; },
(o, r) -> { if(r.getPassed()) o.passed++; else o.failed++; },
(x, y) -> { x.passed += y.passed; x.failed += y.failed; return x; }
)),
m -> m.values().stream()
.filter(o -> o.passed == 0 || o.failed == 0)
.collect(Collectors.partitioningBy(o -> o.failed==0,
Collectors.summingInt(o -> o.failed==0? o.passed: o.failed)))
));
isso só vai contar passed
e failed
, se todos os elementos de um grupo passou ou falhou. Mas desde que você disse, você esperaria 2
e 2
, você pode remover a .filter(o -> o.passed == 0 || o.failed == 0)
linha. Então, ele vai contar passed
apenas se todos os elementos dos grupos passaram, mas contar tudo failed
, mesmo quando alguns elementos do grupo passou. Então, você deseja obter 2
e 2
como resultado.