如何在Scala中使用地图并接收索引?


Answers:


148

我相信您在寻找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

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

您还可以使用以下变体:

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

要么:

List("Mary", "had", "a", "little", "lamb").zipWithIndex.foreach( (t) => println(t._2+" "+t._1) )

4
好吧,我的意思是使用该zipWithIndex方法在循环/映射/任何内容中获取索引。
ziggystar 2012年

例如,比较while循环可能是最快的选择之一。
ziggystar 2012年

一个数组和一个while循环可能最快。
维克多·巴生

2
@ziggystar如果您正在寻找性能,则需要权衡一些不变性。查看zipWithIndex函数内部。它只是使用一个var来构建新的对集合。您可以使用相同的方法来增加var,而无需构建新集合,就像在Java中一样。但这不是功能风格。考虑一下您是否真正需要它。
Cristian Vrabie 2012年

因此,似乎zipWithIndex本身也不是功能样式。无论如何,我认为应该提到的是,使用view您应该能够防止创建和遍历额外的List。
herman 2013年

55

用 。映射到。zipWithIndex

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

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

结果:

List("a(0)", "b(1)", "c(2)")

11
这是我见过的第一个不错的示例,它按需使用map而不是仅在内打印foreach
Greg Chabala

10

所提出的解决方案遭受以下事实的困扰:它们创建了中间集合或引入了并非严格必要的变量。最终,您要做的就是跟踪迭代的步骤数。这可以通过记忆来完成。结果代码可能看起来像

myIterable map (doIndexed(someFunction))

doIndexed-功能包,其接收两个的索引的的元素的内部功能myIterable。您可能对JavaScript很熟悉。

这是实现此目的的一种方法。考虑以下实用程序:

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)
    }
}

这已经是您所需要的。您可以按如下方式应用此方法:

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

结果在列表中

List(97, 99, 101)

这样,您可以使用通常的Traversable函数,而以包装有效函数为代价。开销是创建存储对象及其中的计数器。否则,就内存或性能而言,此解决方案与使用unindexed一样好(或坏)map。请享用!


1
这是一个非常优雅的解决方案。+1(不创建临时集合)。不能在并行集合中工作,但仍然是一个非常好的解决方案。
fbl 2012年

5
如果您不想创建临时集合,请使用coll.view.zipWithIndex代替coll.zipWithIndex
tsuna

5

CountedIterator在2.7.x(你可以从一个正常的迭代器获取与.counted)。我相信它已在2.8中被弃用(或简单地删除了),但是它很容易滚动。您确实需要能够命名迭代器:

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)

3

或者,假设您的集合具有恒定的访问时间,则可以映射索引列表而不是实际的集合:

val ls = List("a","b","c")
0.until(ls.length).map( i => doStuffWithElem(i,ls(i)) )

1
一种更优雅的方法是: ls.indices.map(i => doStuffWithElem(i, ls(i))
Assil Ksiksi

3
@aksiksi好吧,看到它indices已实现,因为0 until length它几乎是同一件事:P
Cristian Vrabie

1
由于复杂性而降低
投票权

1
@MosheBixenshpaner足够公平。我的榜样List确实很差。但是我确实提到过,如果您的收藏集具有恒定的访问时间,则此方法很合适。应该选了Array
克里斯蒂安·克拉比

1

使用.MAP.zipWithIndex与地图数据结构

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

结果

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

1

有两种方法可以做到这一点。

ZipWithIndex:自动创建一个从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")
  }

这两个代码的输出将是:

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

邮编:对流使用邮编方法来创建计数器。这为您提供了一种控制初始值的方法。

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

结果:

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

0

如果您还需要搜索地图值(就像我必须的那样):

val ls = List("a","b","c")
val ls_index_map = ls.zipWithIndex.toMap 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.