Como posso usar o mapa e receber um índice também no Scala?

99

Existe alguma lista / sequência embutida que se comporta como mape fornece o índice do elemento também?

Geo
fonte

Respostas:

148

Eu acredito que você está procurando por zipWithIndex?

scala> val ls = List("Mary", "had", "a", "little", "lamb")
scala> ls.zipWithIndex.foreach{ case (e, i) => println(i+" "+e) }
0 Mary
1 had
2 a
3 little
4 lamb

De: http://www.artima.com/forums/flat.jsp?forum=283&thread=243570

Você também tem variações como:

for((e,i) <- List("Mary", "had", "a", "little", "lamb").zipWithIndex) println(i+" "+e)

ou:

List("Mary", "had", "a", "little", "lamb").zipWithIndex.foreach( (t) => println(t._2+" "+t._1) )
Viktor Klang
fonte
4
Bem, eu quis dizer usar o zipWithIndexmétodo para obter o índice dentro de um loop / mapa / qualquer coisa.
ziggystar
Por exemplo, comparar a um whileloop, que provavelmente está entre as opções mais rápidas.
ziggystar
Um Array e um loop while são provavelmente o mais rápido possível.
Viktor Klang,
2
@ziggystar Se você está procurando desempenho, precisa trocar alguma imutabilidade. Olhe dentro da função zipWithIndex. Ele apenas usa uma var para construir uma nova coleção de pares. Você pode usar o mesmo método de incrementar uma var sem construir a nova coleção, assim como faria em Java. Mas não é um estilo funcional. Pense se você realmente precisa disso.
Cristian Vrabie
Portanto, parece que o próprio zipWithIndex não é um estilo funcional. De qualquer forma, acho que deve ser mencionado que usando viewvocê deve ser capaz de evitar a criação e percorrer uma lista extra.
herman
55

Usar . mapa em. zipWithIndex

val myList = List("a", "b", "c")

myList.zipWithIndex.map { case (element, index) => 
   println(element, index) 
   s"${element}(${index})"
}

Resultado:

List("a(0)", "b(1)", "c(2)")
Paulo Cheque
fonte
11
Este é o primeiro exemplo decente que vi que usou a mapconforme solicitado em vez de apenas imprimir dentro de a foreach.
Greg Chabala
10

As soluções propostas sofrem do fato de criarem coleções intermediárias ou introduzirem variáveis ​​que não são estritamente necessárias. Afinal, tudo o que você precisa fazer é controlar o número de etapas de uma iteração. Isso pode ser feito usando memoizing. O código resultante pode parecer

myIterable map (doIndexed(someFunction))

o doIndexed -Function envolve a função interior que recebe um índice e os elementos de myIterable. Isso pode ser familiar para você do JavaScript.

Aqui está uma maneira de atingir esse propósito. Considere o seguinte utilitário:

object TraversableUtil {
    class IndexMemoizingFunction[A, B](f: (Int, A) => B) extends Function1[A, B] {
        private var index = 0
        override def apply(a: A): B = {
            val ret = f(index, a)
            index += 1
            ret
        }
    }

    def doIndexed[A, B](f: (Int, A) => B): A => B = {
        new IndexMemoizingFunction(f)
    }
}

Isso já é tudo que você precisa. Você pode aplicar isso, por exemplo, da seguinte forma:

import TraversableUtil._
List('a','b','c').map(doIndexed((i, char) => char + i))

que resulta na lista

List(97, 99, 101)

Desta forma, você pode usar as funções normais de Traversable à custa de empacotar sua função efetiva. A sobrecarga é a criação do objeto memoizing e do contador nele. Caso contrário, esta solução é tão boa (ou ruim) em termos de memória ou desempenho quanto usar não indexado map. Aproveitar!

Matt Brasen
fonte
1
Esta é uma solução muito elegante para o problema. 1 por não criar uma coleção temporária. Não funcionará em uma coleção paralela, mas ainda é uma solução muito boa.
fbl
5
Se você não quiser criar uma coleção temporária, basta usar em coll.view.zipWithIndexvez decoll.zipWithIndex
tsuna
5

Existe CountedIteratorem 2.7.x (que você pode obter de um iterador normal com .counted). Eu acredito que ele foi descontinuado (ou simplesmente removido) no 2.8, mas é fácil de fazer o seu próprio. Você precisa ser capaz de nomear o iterador:

val ci = List("These","are","words").elements.counted
scala> ci map (i => i+"=#"+ci.count) toList
res0: List[java.lang.String] = List(These=#0,are=#1,words=#2)
Rex Kerr
fonte
3

Ou, supondo que sua coleção tenha tempo de acesso constante, você pode mapear a lista de índices em vez da coleção real:

val ls = List("a","b","c")
0.until(ls.length).map( i => doStuffWithElem(i,ls(i)) )
Cristian Vrabie
fonte
1
Uma maneira mais elegante de fazer isso é simplesmente: ls.indices.map(i => doStuffWithElem(i, ls(i))
Assil Ksiksi
3
@aksiksi Bem, vendo que indicesestá implementado0 until length é praticamente a mesma coisa: P
Cristian Vrabie
1
votar negativamente por causa da complexidade - iterar com ls (i) leva O (n ^ 2)
Moshe Bixenshpaner
1
@MosheBixenshpaner Bastante justo. Meu exemplo de Listera realmente pobre. Eu mencionei, no entanto, que isso é adequado se sua coleção tiver tempo de acesso constante. Deveria ter escolhido Array.
Cristian Vrabie
1

Use .map em .zipWithIndex com estrutura de dados Map

val sampleMap = Map("a" -> "hello", "b" -> "world", "c" -> "again")

val result = sampleMap.zipWithIndex.map { case ((key, value), index) => 
    s"Key: $key - Value: $value with Index: $index"
}

Resultados

 List(
       Key: a - Value: hello with Index: 0, 
       Key: b - Value: world with Index: 1, 
       Key: c - Value: again with Index: 2
     )
fgfernandez0321
fonte
1

Há duas maneiras de fazer isso.

ZipWithIndex: Cria um contador automaticamente começando com 0.

  // zipWithIndex with a map.
  val days = List("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat")
  days.zipWithIndex.map {
        case (day, count) => println(s"$count is $day")
  }
  // Or use it simply with a for.
  for ((day, count) <- days.zipWithIndex) {
        println(s"$count is $day")
  }

A saída de ambos os códigos será:

0 is Sun
1 is Mon
2 is Tue
3 is Wed
4 is Thu
5 is Fri
6 is Sat

Zip : Use o método zip com um Stream para criar um contador. Isso fornece uma maneira de controlar o valor inicial.

for ((day, count) <- days.zip(Stream from 1)) {
  println(s"$count is $day")
}

Resultado:

1 is Sun
2 is Mon
3 is Tue
4 is Wed
5 is Thu
6 is Fri
7 is Sat
Madstuffs
fonte
0

Se você precisar pesquisar os valores do mapa também (como eu tive):

val ls = List("a","b","c")
val ls_index_map = ls.zipWithIndex.toMap 
Yair Beer
fonte