如何从Scala中的列表中单独“删除”单个元素并缩小差距?

时间:2012-10-12 17:49:23

标签: list scala

列表在Scala中是不可变的,所以我试图找出如何“删除” - 实际上,创建一个新的集合 - 该元素然后关闭列表中创建的间隙。这听起来像是一个使用地图的好地方,但我不知道如何开始这个实例。

课程是一个字符串列表。我需要这个循环,因为我实际上有几个列表,我需要删除该索引处的元素(我使用多个列表来存储列表中的数据,我只是通过简单地确保索引将始终这样做跨列表对应)。

  for (i <- 0 until courses.length){
    if (input == courses(i) {
    //I need a map call on each list here to remove that element
    //this element is not guaranteed to be at the front or the end of the list
    }
  }
}

让我为问题添加一些细节。我有四个列表,通过索引相互关联;一个列表存储课程名称,一个存储类以简单的int格式开始的时间(即130),一个存储“am”或“pm”,一个存储类的日期(所以“MWF” evals to 1,“TR”evals to 2,等等。我不知道是否有多个这是解决这个问题的最佳或“正确”的方法,但这些都是我所拥有的工具(自从我16岁以来没有认真编程的第一年comp sci学生)。我正在编写一个函数来从每个列表中删除相应的元素,我所知道的是1)索引对应,2)用户输入课程名称。如何使用filterNot从每个列表中删除相应的元素?我不认为我对每个列表都有足够的了解,可以使用更高阶的函数。

6 个答案:

答案 0 :(得分:12)

这是filter的用例:

scala> List(1,2,3,4,5)
res0: List[Int] = List(1, 2, 3, 4, 5)

scala> res0.filter(_ != 2)
res1: List[Int] = List(1, 3, 4, 5)

您希望在转换列表的所有元素时使用map。

答案 1 :(得分:11)

要直接回答您的问题,我认为您正在寻找patch,例如删除索引为2的元素(“c”):

List("a","b","c","d").patch(2, Nil, 1)      // List(a, b, d)

其中Nil是我们要替换的内容,1是要替换的字符数。

但是,如果你这样做:

  

我有四个按索引相互关联的列表;一   list存储课程名称,一个存储课程开始的时间   一个简单的int格式(即130),一个存储“am”或“pm”,以及一个   按int

存储类的日期
你将度过一段美好时光。我建议你使用case class

case class Course(name: String, time: Int, ampm: String, day: Int)

然后将它们存储在Set[Course]中。 (将时间和天数存储为Int也不是一个好主意 - 请查看java.util.Calendar。)

答案 2 :(得分:3)

首先是几个旁注:

  1. List不是基于索引的结构。所有面向索引的操作都需要线性时间。对于面向索引的算法,Vector是一个更好的候选者。事实上,如果你的算法需要索引,那么你肯定不会暴露Scala的功能。

  2. map用于将项目“A”的集合转换为相同的项目集合“B”,使用从单个“A”到单个“B”的传入变换器函数。它不能改变结果元素的数量。您可能会将mapfoldreduce混淆。

  3. 回答您更新的问题

    好的,这是一个功能性解决方案,可以在列表上有效运行:

    val (resultCourses, resultTimeList, resultAmOrPmList, resultDateList)
      = (courses, timeList, amOrPmList, dateList)
          .zipped
          .filterNot(_._1 == input)
          .unzip4
    

    但是有一个问题。实际上,我惊讶地发现,在这个解决方案中使用的函数是功能语言的基础,它们不存在于标准的Scala库中。 Scala有2个和3个元组,但不是其他元组。

    要解决此问题,您需要导入以下隐式扩展名。

    implicit class Tuple4Zipped 
      [ A, B, C, D ] 
      ( val t : (Iterable[A], Iterable[B], Iterable[C], Iterable[D]) ) 
      extends AnyVal 
      {
        def zipped 
          = t._1.toStream
              .zip(t._2).zip(t._3).zip(t._4)
              .map{ case (((a, b), c), d) => (a, b, c, d) }
      }
    
    implicit class IterableUnzip4
      [ A, B, C, D ]
      ( val ts : Iterable[(A, B, C, D)] )
      extends AnyVal
      {
        def unzip4
          = ts.foldRight((List[A](), List[B](), List[C](), List[D]()))(
              (a, z) => (a._1 +: z._1, a._2 +: z._2, a._3 +: z._3, a._4 +: z._4)
            )
      }
    

    此实现需要Scala 2.10,因为它利用新的有效值类功能来对现有类型进行拉皮条。

    我实际上将这些内容包含在一个名为SExt的小型扩展库中,在依赖您的项目之后,只需添加import sext._语句就可以获得它们。

    当然,如果您愿意,您可以直接将这些功能组合到解决方案中:

    val (resultCourses, resultTimeList, resultAmOrPmList, resultDateList)
      = courses.toStream
          .zip(timeList).zip(amOrPmList).zip(dateList)
          .map{ case (((a, b), c), d) => (a, b, c, d) }
          .filterNot(_._1 == input)
          .foldRight((List[A](), List[B](), List[C](), List[D]()))(
            (a, z) => (a._1 +: z._1, a._2 +: z._2, a._3 +: z._3, a._4 +: z._4)
          )
    

答案 3 :(得分:1)

删除和过滤列表元素

在Scala中,您可以过滤列表以删除元素。

scala> val courses = List("Artificial Intelligence", "Programming Languages", "Compilers", "Networks", "Databases")
courses: List[java.lang.String] = List(Artificial Intelligence, Programming Languages, Compilers, Networks, Databases)

让我们删除几个类:

courses.filterNot(p => p == "Compilers" || p == "Databases")

你也可以使用删除但不推荐使用过滤器或filterNot。

如果要通过索引删除,可以使用zipWithIndex将列表中的每个元素与有序索引相关联。因此,courses.zipWithIndex变为:

List[(java.lang.String, Int)] = List((Artificial Intelligence,0), (Programming Languages,1), (Compilers,2), (Networks,3), (Databases,4))

要从中删除第二个元素,您可以使用courses.filterNot(_._2 == 1)来引用元组中的索引,该列表给出了列表:

res8: List[(java.lang.String, Int)] = List((Artificial Intelligence,0), (Compilers,2), (Networks,3), (Databases,4))

最后,另一个工具是使用indexWhere来查找任意元素的索引。

courses.indexWhere(_ contains "Languages") res9: Int = 1

重新更新

  

我正在编写一个函数来从每个元素中删除相应的元素   列表,我所知道的是1)索引对应和2)   用户输入课程名称。我该如何删除相应的   每个列表中的元素使用filterNot?

与Nikita的更新类似,您必须“合并”每个列表的元素。因此,需要将课程,经验,日期和时间放入元组或类中以保存相关元素。然后,您可以过滤元组的元素或类的字段。

使用此示例数据将相应元素组合到元组中如下所示:

val courses = List(Artificial Intelligence, Programming Languages, Compilers, Networks, Databases)
val meridiems = List(am, pm, am, pm, am)
val times = List(100, 1200, 0100, 0900, 0800)
val days = List(MWF, TTH, MW, MWF, MTWTHF)

将它们与zip结合使用:

courses zip days zip times zip meridiems

val zipped = List[(((java.lang.String, java.lang.String), java.lang.String), java.lang.String)] = List((((Artificial Intelligence,MWF),100),am), (((Programming Languages,TTH),1200),pm), (((Compilers,MW),0100),am), (((Networks,MWF),0900),pm), (((Databases,MTWTHF),0800),am))

这种憎恶使嵌套的元组变平为一个元组。有更好的方法。

zipped.map(x => (x._1._1._1, x._1._1._2, x._1._2, x._2)).toList

一个很好的元组列表。

List[(java.lang.String, java.lang.String, java.lang.String, java.lang.String)] = List((Artificial Intelligence,MWF,100,am), (Programming Languages,TTH,1200,pm), (Compilers,MW,0100,am), (Networks,MWF,0900,pm), (Databases,MTWTHF,0800,am))

最后,我们可以使用filterNot根据课程名称进行过滤。例如filterNot(_._1 == "Networks")

List[(java.lang.String, java.lang.String, java.lang.String, java.lang.String)] = List((Artificial Intelligence,MWF,100,am), (Programming Languages,TTH,1200,pm), (Compilers,MW,0100,am), (Databases,MTWTHF,0800,am))

答案 4 :(得分:0)

我即将给出的答案可能超越了你在课程中所教授的内容,所以如果是这种情况我会道歉。

首先,你有权质疑你是否应该有四个清单 - 从根本上说,它听起来像你需要的是一个代表一个过程的对象:

/**
 * Represents a course.
 * @param name the human-readable descriptor for the course
 * @param time the time of day as an integer equivalent to 
 *             12 hour time, i.e. 1130
 * @param meridiem the half of the day that the time corresponds 
 *                 to: either "am" or "pm"
 * @param days an encoding of the days of the week the classes runs.
 */
case class Course(name : String, timeOfDay : Int, meridiem : String, days : Int)

您可以使用它来定义单独的课程

val cs101 = 
  Course("CS101 - Introduction to Object-Functional Programming", 
         1000, "am", 1)

有更好的方法来定义这种类型(更好地表示12小时的时间,更清晰的方式来表示星期几等),但我不会偏离原来的问题陈述。

鉴于此,您将拥有一个课程列表:

val courses = List(cs101, cs402, bio101, phil101)

如果您想查找并删除与给定名称匹配的所有课程,您可以写下:

val courseToRemove = "PHIL101 - Philosophy of Beard Ownership"
courses.filterNot(course => course.name == courseToRemove)

等效地,在Scala中使用下划线语法糖来表示函数文字:

courses.filterNot(_.name == courseToRemove)

如果存在多个课程可能具有相同名称的风险(或者您使用正则表达式或前缀匹配基于某些部分条件进行过滤)并且您只想删除第一个匹配项,那么您可以定义你自己的功能:

def removeFirst(courses : List[Course], courseToRemove : String) : List[Course] =
  courses match {
    case Nil => Nil
    case head :: tail if head == courseToRemove => tail
    case head :: tail => head :: removeFirst(tail)
  }

答案 5 :(得分:0)

使用ListBuffer是一个像java列表一样的可变列表

 var l =  scala.collection.mutable.ListBuffer("a","b" ,"c")
 print(l) //ListBuffer(a, b, c)
 l.remove(0)
 print(l) //ListBuffer(b, c)