Scala 如何在列表中替换或追加项目?

Scala 如何在列表中替换或追加项目?,scala,collections,Scala,Collections,假设我有一个案例类a(id:Int,str:String)的列表和一个a的实例。我需要用新实例替换列表中的项目,或者将新实例附加到列表中 case class A(id: Int, str: String) def replaceOrAppend(as: List[A], a: A): List[A] = ??? val as = List(A(1, "a1"), A(2, "a2"), A(3, "a3")) replaceOrAppend(as, A(2, "xyz")) // List(

假设我有一个
案例类a(id:Int,str:String)
的列表和一个
a
的实例。我需要用新实例替换列表中的项目,或者将新实例附加到列表中

case class A(id: Int, str: String)
def replaceOrAppend(as: List[A], a: A): List[A] = ???

val as = List(A(1, "a1"), A(2, "a2"), A(3, "a3"))
replaceOrAppend(as, A(2, "xyz")) // List(A(1, "a1"), A(2, "xyz"), A(3, "a3"))
replaceOrAppend(as, A(5, "xyz")) // List(A(1, "a1"), A(2, "a2"), A(3, "a3"), A(5, "xyz"))
我可以这样写
replace或append

def replaceOrAppend(as: List[A], a: A): List[A] =
  if (as.exists(_.id == a.id)) as.map(x => if (x.id == a.id) a else x) else as :+ a 

这个实现有点笨拙,而且显然是次优的,因为它两次传递输入列表。如何实现
replace或append
只传递一次输入列表?

如果顺序不是必需的,我会选择:

def replaceOrAppend(as: List[A], a: A): List[A] =
  a::as.filterNot(_.id == a.id) 
如果订单与
id
str
相关,这也会起作用:

def replaceOrAppend(as: List[A], a: A): List[A] =
  (a::as.filterNot(_.id == a.id)).sortBy(_.id)
如果必须遵守命令(正如Micheal所建议的——我找不到更好的):


如果订单不是必需的,我会选择:

def replaceOrAppend(as: List[A], a: A): List[A] =
  a::as.filterNot(_.id == a.id) 
如果订单与
id
str
相关,这也会起作用:

def replaceOrAppend(as: List[A], a: A): List[A] =
  (a::as.filterNot(_.id == a.id)).sortBy(_.id)
如果必须遵守命令(正如Micheal所建议的——我找不到更好的):

这里还有一个:

def replaceOrAppend(as:List[A],A:A):List[A]={
as.find(u.id==a.id).map(op=>{
as.map(el=>el匹配{
案例e如果e.id==a.id=>e.copy(str=a.str)
案例=>el
})
}).getOrElse((a::as.reverse).reverse)
}
这里是另一个:

def replaceOrAppend(as:List[A],A:A):List[A]={
as.find(u.id==a.id).map(op=>{
as.map(el=>el匹配{
案例e如果e.id==a.id=>e.copy(str=a.str)
案例=>el
})
}).getOrElse((a::as.reverse).reverse)
}

这个怎么样?仍然笨拙,但只使用一次迭代

  def replaceOrAppend(as: List[A], a: A): List[A] = {
    val (updatedList,itemToAppend) = as.foldLeft((List[A](),Option(a))) {
      case ((acc, Some(item)), l) =>
        if (item.id == l.id) (acc :+ item, None)
        else (acc :+ l, Some(item))
      case ((acc, None), l) => (acc :+ l, None)
    }
    itemToAppend match {
      case Some(item) => updatedList :+ item
      case None => updatedList
    }
  }

这个怎么样?仍然笨拙,但只使用一次迭代

  def replaceOrAppend(as: List[A], a: A): List[A] = {
    val (updatedList,itemToAppend) = as.foldLeft((List[A](),Option(a))) {
      case ((acc, Some(item)), l) =>
        if (item.id == l.id) (acc :+ item, None)
        else (acc :+ l, Some(item))
      case ((acc, None), l) => (acc :+ l, None)
    }
    itemToAppend match {
      case Some(item) => updatedList :+ item
      case None => updatedList
    }
  }

我不明白为什么人们忘记了处理函数列表的最佳方法是通过模式匹配+尾部递归
嗯,这看起来更干净,并且尽可能地提高效率

final case class A(id: Int, str: String)

def replaceOrAppend(as: List[A], a: A): List[A] = {
  @annotation.tailrec
  def loop(remaining: List[A], acc: List[A]): List[A] =
    remaining match {
      case x :: xs if (x.id == a.id) =>
        acc reverse_::: (a :: xs)

      case x :: xs =>
        loop(remaining = xs, acc = x :: acc)

      case Nil =>
        (a :: acc).reverse
    }

  loop(remaining = as, acc = List.empty)
}
从技术上讲,在最坏的情况下,这将遍历列表两次。

但是,通过从头开始,最后倒转来建立一个列表总是比做许多附加更好。

我不明白为什么人们忘记了处理函数列表的最佳方法是通过模式匹配+尾部递归
嗯,这看起来更干净,并且尽可能地提高效率

final case class A(id: Int, str: String)

def replaceOrAppend(as: List[A], a: A): List[A] = {
  @annotation.tailrec
  def loop(remaining: List[A], acc: List[A]): List[A] =
    remaining match {
      case x :: xs if (x.id == a.id) =>
        acc reverse_::: (a :: xs)

      case x :: xs =>
        loop(remaining = xs, acc = x :: acc)

      case Nil =>
        (a :: acc).reverse
    }

  loop(remaining = as, acc = List.empty)
}
从技术上讲,在最坏的情况下,这将遍历列表两次。


但是,通过从头开始,然后在末尾反向来建立一个列表总比做许多附加操作要好。

考虑到
id
是一个键,为什么不使用
Map
而不是
list
…让我们假设
replaceOrAppend
签名已经给出。当然,您可以将列表转换为地图,将元素添加到地图,然后再次将地图转换为列表。我只是不确定这是否是一个更好的方法。为什么你需要一个列表,顺序重要吗?这看起来是一个X/Y问题(假设
List
是正确的方法…@LuisMiguelMejíaSuárez是的,输入列表顺序很重要。将
id
作为一个键,为什么不使用
映射
而不是
列表
…让我们假设已给出
替换或显示
签名。当然,您可以将列表转换为地图,将元素添加到地图,然后再次将地图转换为列表。我只是不确定这是否是一个更好的方法。为什么你需要一个列表,顺序重要吗?这看起来是一个X/Y问题(假设
List
是正确的方式…@LuisMiguelMejíaSuárez是的,输入列表顺序很重要。谢谢,但是如果输入列表顺序很重要呢?那么如果附加了a,那么a总是在末尾?是的,
a
如果被追加,应该在
as
的末尾。你对
def replaceOrAppend(as:List[a],a:a):List[a]=as.span(u.id!=a.id)match{case(xs,y::ys)=>xs xs++(a+:ys);case(xs,Nil)=>xs a}
?如果必须遵守顺序,我想这是你能做的最好的了——我把它添加到了我的答案中;)。谢谢,但是如果输入列表顺序真的很重要怎么办?那么如果追加,A总是在末尾吗?是的,
A
如果追加,应该在
as
的末尾。你怎么看
def replaceOrAppend(as:list[A],A:A):list[A]=as.span(u.id!=A.id)match{case(xs,y::ys)=>xs++(A+:ys);case(xs Nil)=>xs A}
?如果必须遵守订单,我认为这是您能做的最好的事情-我将其添加到我的答案中;)。看起来您的函数传递了两次输入列表,但我要求一个函数传递一次输入。不错的尝试:)看起来你的函数传递了两次输入列表,但我要求一个函数传递一次输入。不错的尝试:)谢谢。看起来确实很笨拙,但也许我们可以改进一下。谢谢。看起来确实很笨拙,但也许我们可以改进它。