Transforma el bucle while en cierre en Groovy

Oct 20 2020

Cómo transformar el siguiente código while en groovy en cierres geniales. Es posible ?

 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"] 

Pensé en la siguiente solución, pero parece que no funciona:

 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"] 

Recibo este comentario en mi código anterior que " index = index + 2nunca se usa"

Respuestas

3 MichaelEaster Oct 20 2020 at 18:52

Considere el injectmétodo (aunque esto muta elementos de la lista, lo que parece arriesgado):

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"]

Aquí, accestá la "lista de resultados acumulados" y itemes el elemento actual en la lista original. A partir de aquí : el injectmétodo también se conoce como reduceo folden otros idiomas.

2 MatiasBjarland Oct 20 2020 at 22:54

Otra respuesta similar a la de Michael Easter con inyectar:

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"]

quizás con algunos personajes menos.

1 MichaelEaster Oct 20 2020 at 19:36

Aquí está mi segunda solución. No utiliza cierres, pero podría decirse que es "funcional". Tenga en cuenta que no es una gran idea 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

No estoy seguro de si esta es la solución más moderna de todas, pero funciona para su ejemplo:

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 regresa [s1, s2, s3:s4, s5:]como se esperaba

Procesas cada elemento de la lista y

si el elemento anterior existe y tiene el sufijo: no hacer nada

si el elemento actual tiene el sufijo y no es el último: concatenar el elemento actual con el siguiente.