Transforme o loop while em encerramento no Groovy

Oct 20 2020

Como transformar o seguinte código while em groovy em closures groovy. É possível ?

 List<String> list = ["s1", "s2", "s3:", "s4", "s5"]
 List<String> res = []
    int index = 0
    while (index < res.size()) {
      String cur = list[index]
      if (cur.endsWith(":")) {
        String next = list[index + 1] /*out of bounds won't happen*/
        res.add(cur + "\n" + next)
        index = index + 2
      } else {
        res.add(cur)
        index++
      }
    }
assert res == ["s1", "s2", "s3:s4", "s5"] 

Pensei na seguinte solução, mas parece que não está funcionando:

 List<String> list = ["s1", "s2", "s3:", "s4", "s5"]
 List<String> res = list.eachWithIndex { int index, String cur ->
   if (cur.endsWith(":")) {
     String next = list[index + 1] /*Out of bounds won't happen*/
     index = index + 2
     return cur + "\n" + next
   }
   return cur
 } as List<String>
assert res == ["s1", "s2", "s3:s4", "s5"] 

Recebo esta observação no meu código acima de que " index = index + 2nunca é usado"

Respostas

3 MichaelEaster Oct 20 2020 at 18:52

Considere o injectmétodo (embora isso modifique elementos da lista, o que parece arriscado):

def list = ["s1", "s2", "s3:", "s4", "s5"]

def res = list.inject([], { acc, item ->
    def lastItem = acc.isEmpty() ? null : acc.last()

    if (lastItem?.endsWith(":")) {
        acc[-1] += item
    } else {
        acc << item
    }

    acc
}) 

assert res == ["s1", "s2", "s3:s4", "s5"]

Aqui, accestá a "lista de resultados acumulados" e itemé o item atual na lista original. A partir daqui : o injectmétodo também é conhecido como reduceou foldem outras línguas.

2 MatiasBjarland Oct 20 2020 at 22:54

Outra resposta semelhante à de Michael Easter com injetar:

def list = ["s1", "s2", "s3:", "s4", "s5"]

def res = list.inject([]) { acc, item ->
  if (acc && acc[-1].endsWith(':')) 
    item = acc.removeLast() + item

  acc + item
}

assert res == ["s1", "s2", "s3:s4", "s5"]

com talvez alguns personagens a menos.

1 MichaelEaster Oct 20 2020 at 19:36

Aqui está minha segunda solução. Não usa fechamentos, mas é indiscutivelmente "funcional". Observe que não é uma boa ideia para listas grandes:

def list = ["s1", "s2", "s3:", "s4", "s5"]

def str = list.join(",")
// e.g. s1,s2,s3:,s4,s5

str = str.replaceAll(/\:,/, ":")
// e.g. s1,s2,s3:s4,s5

def res = str.split(",")
assert res == ["s1", "s2", "s3:s4", "s5"]
1 MarmiteBomber Oct 20 2020 at 15:04

Não tenho certeza se este é o grooviest de toda solução existente, mas funciona para o seu exemplo:

list = ["s1", "s2", "s3:", "s4", "s5:"]

res = []

list.eachWithIndex 
    {e, i -> if (i > 0 && list[i-1].endsWith(":")) {return} 
             if (e.endsWith(":") && i <  list.size-1) {res.add("${e}${list[i+1]}") } else {res.add(e)}}

que retorna [s1, s2, s3:s4, s5:]como esperado

Você processa cada elemento da lista e

se o elemento anterior existe e é sufixado: não faça nada

se o elemento atual tiver o sufixo e não for o último: concatene o elemento atual com o próximo.