【问题标题】:Is there a native grouping function that works like the sortWith function?是否有像 sortWith 函数一样工作的本机分组函数?
【发布时间】:2016-03-10 06:10:44
【问题描述】:

有一些库(例如 Spark 和其他 Scala 扩展)具有可用的“groupWith”函数。此功能允许您将一个元素与集合的其余部分进行比较,然后使用一个或多个谓词对其进行分组。 Scala 中似乎没有为此提供任何本机功能,但它们确实具有行为相似的 sortWith 函数,但仅对项目进行排序而不是对它们进行分组。如果解释不充分,这里有一个小代码示例,应该显示我正在尝试做的事情:

val list = List(1,2,3,4,5,5)
val groupedList = list.groupWith{ (e,c) =>
    e == c
}

这是一个很简单的例子,我想做更复杂的比较比如

e + 1 == c

那么问题是有没有任何原生的 Scala 函数可以做到这一点?有什么建议或解决方法吗?

更新: 从给出的简单示例看来,我要做什么并不完全清楚,这是一个更好的示例: 假设我有一个案例类和这些对象的列表:

case class Item(num: Int, color: String)
val list = List(new Item(13, "red"), new Item(14,"red"), new Item(15, "blue"), new Item(16, "red"))

list.groupWith{ (e,c) =>
    (e.num -1 == c.num || e.num + 1 == c.num ) && e.color == c.color        
}

这应该返回如下内容:

res8: List[List[Item]] = List(List(Item(13,red), Item(14,red)), List(Item(15,blue)), List(Item(16,red)))

【问题讨论】:

  • 你告诉我们,那些不熟悉 Spark groupWith 的人,你对这两种情况的结果都是groupedList
  • 我缺少groupWith 的语义。
  • 你只是想要成对分组,它们是按顺序排列的,即对于 (1, 3, 2, 3) 和 (e, e+1) 是否返回 (1, 2), (2 ,3), (2,3) 还是只是 (2,3)?
  • 如果与组中的任何现有成员相比,如果它们通过谓词,那么事情最终会在组中吗?这显然是 O(N^2),可以吗?
  • 是的,这与其说是生产代码问题,不如说是一个好奇心,如果您查看本机 sortWith 函数,我想它具有相同的复杂性。

标签: scala


【解决方案1】:

这是一个实现:

// Takes the list as a parameter, can use pimp-my-library if you want
def groupWith[A](xs: List[A], f: (A, A) => Boolean) = {
  // helper function to add "e" to any list with a member that matches the predicate
  // otherwise add it to a list of its own
  def addtoGroup(gs: List[List[A]], e: A): List[List[A]] = {
    val (before, after) = gs.span(_.exists(!f(_, e)))
    if (after.isEmpty)
      List(e) :: gs
    else
      before ::: (e :: after.head) :: after.tail
  }
  // now a simple foldLeft adding each element to the appropriate list
  xs.foldLeft(Nil: List[List[A]])(addtoGroup)
} 

groupWith(list, { (e: Item, c: Item) =>
                    (e.num - 1 == c.num || e.num + 1 == c.num) && e.color == c.color})

//| res0: List[List[groups.groups.Item]] =
//         List(List(Item(16,red)),
//              List(Item(15 ,blue)), 
//              List(Item(14,red), Item(13,red)))

【讨论】:

  • 这种方法绝对符合函数需要做什么的描述,但我正在寻找 Scala 原生的东西(即我不需要自己编写的东西)。
  • 好吧,你不需要自己写。我为你写的 :) 据我所知,标准库中没有任何东西可以完全满足你的要求。
  • 没错,我想这本身就回答了我的问题,这是我将要找到的最好的答案,谢谢!
【解决方案2】:

不确定这是否是您想要的(检查我的 cmets 到您的问题),但在 GenTraversableLike 中定义的方法 groupBy List 继承(不仅是 List)。你会得到:

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

scala> list.groupBy( el => el )
res0: scala.collection.immutable.Map[Int,List[Int]] = Map(5 -> List(5, 5), 1 -> List(1), 2 -> List(2), 3 -> List(3), 4 -> List(4))

scala> list.groupBy( el => el + 1 )
res1: scala.collection.immutable.Map[Int,List[Int]] = Map(5 -> List(4), 6 -> List(5, 5), 2 -> List(1), 3 -> List(2), 4 -> List(3))

基本上你需要提供从值到键的鉴别函数,你会得到Map[Key, List[Value]

这是你想要的吗?

【讨论】:

  • 不完全是,我确实知道 groupBy 函数,但它只允许您与元素本身进行比较,因此基于数字接近度的分组操作是不可能的(我认为)。请参阅我的更新,以更清楚地了解我正在寻找的内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-03-24
  • 2018-03-31
  • 2021-04-19
  • 2019-11-28
  • 2016-08-12
  • 1970-01-01
  • 2023-03-23
相关资源
最近更新 更多