Scala:比较数组忽略顺序

Scala: Compare Arrays ignoring order

提问人:dev 提问时间:9/30/2020 最后编辑:dev 更新时间:11/24/2022 访问量:2046

问:

我想知道数组中是否有任何方法可以检查忽略顺序的相等性。到目前为止,我确实找到了这个:

  test("test ignoring order"){
    assert(Array(1,2,4,5).sameElements(Array(1,4,2,5)))
  }

但它失败了,因为顺序不同:

org.scalatest.exceptions.TestFailedException: scala.Predef.intArrayOps(scala.Array.apply(1, 2, 4, 5)).sameElements[Int](scala.Predef.wrapIntArray(scala.Array.apply(1, 4, 2, 5))) was false

有没有办法做到这一点,在数组内部或外部?

编辑:我不需要对数组进行排序,我只想比较它们而忽略顺序。

数组 scala 相等

评论

4赞 Luis Miguel Mejía Suárez 9/30/2020
如果您使用的是 Scalatest,请尝试使用,我也建议您不要使用 Arrays,而是使用 ListVector 等真正的集合。- 最后,您也可以将它们转换为集合(如果您不关心每个元素出现的次数)或在两者上使用来比较两个出现映射。Array(1,2,4,5) contain theSameElementsAs Array(1,2,4,5)sortgroupMapReduce(identity)(_ => 1)(_ + _)
0赞 dev 9/30/2020
感谢您@LuisMiguelMejíaSuárez的评论。我不想对它们进行排序,我需要使用数组。如何导入 Scalatest?你能在问题中放一个像我这样的单元测试示例吗?(从未使用过 ScalaTest)
0赞 Luis Miguel Mejía Suárez 10/1/2020
您使用的是哪种测试框架?为什么需要数组?为什么您不想对它们或任何其他替代方案进行排序?(请注意,任何进行此类比较的方法都在后台做类似的事情)
0赞 dev 10/1/2020
我正在使用 FunSuite 作为框架
3赞 Luis Miguel Mejía Suárez 10/1/2020
FunSuite 不是一个框架,FunSuite Scaltest 的类之一。首先了解更多信息并了解您正在使用的内容可能对您有好处。

答:

1赞 lprakashv 10/1/2020 #1

下面将对两个数组进行排序,然后将它们相等:

test("test ignoring order"){
  assert(Array(1,2,4,5).sorted sameElements Array(1,4,2,5).sorted)
}

笔记:

  1. 如果您正在处理除 . 之外的其他一些集合,则可以使用 instead 代替。==sameElementsArray
  2. array1.toSet == array2.toSet如果其中一个数组有重复项而另一个数组没有,则不起作用。

评论

0赞 dev 10/1/2020
感谢您的回答,但不幸的是,我想忽略顺序来比较它们,而不对它们进行排序
4赞 Tim 10/1/2020
@dev 对它们进行排序有什么问题?这是一个测试,而不是主线代码。
1赞 Maestro 10/1/2020 #2

这是否按预期工作?


import scala.annotation.tailrec
def equalsIgnoringOrder(first:Array[Int], second:Array[Int]) : Boolean = {

  def removeAtIndex(i:Int, array: Array[Int]) : Array[Int] = {
    val buffer = array.toBuffer
    buffer.remove(i)
    buffer.toArray
  }

  @tailrec
  def firstEqualSecondRec(i:Int, other:Array[Int]) : Boolean = {
    if(other.isEmpty) true
    else {
      val el = first(i)
      val index = other.indexOf(el)
      if(index == -1) false
      else firstEqualSecondRec(i+1, removeAtIndex(index, other))
    }
  }

  if (first.length != second.length) false
  else {
    val startingIndex = 0
    firstEqualSecondRec(startingIndex, second)
  }
}
2赞 jwvh 10/2/2020 #3

一个简单的递归就可以了。

def isSame[T](arrA:Array[T], arrB:Array[T]) :Boolean =
  arrA.length == arrB.length &&
    (arrA.isEmpty || isSame(arrA.filterNot(_ == arrA.head)
                           ,arrB.filterNot(_ == arrA.head)))

但@Tim的问题是有道理的:你对明显而简单的排序解决方案有什么反对意见?

评论

1赞 Tim 10/2/2020
好。可以避免裸露headarrA.length == arrB.length && arrA.headOption.forall(x => isSame(arrA.filterNot(_ == x), arrB.filterNot(_ == x)))
0赞 jwvh 10/2/2020
@Tim;我喜欢。我们放弃了尾递归,但这是一件小事。
0赞 Tim 10/2/2020
关于尾递归的好点。归根结底,这取决于这样做的动机是什么,目前尚不清楚。
3赞 Joshua Esolk 11/24/2022 #4

这是一个较旧的线程,但我只是有同样的问题。

建议的答案包括排序(仅适用于可比较的对象)或具有 O(n^2) 运行时行为(和/或非平凡递归)的方法。

另一种(简单但易于理解且功能强大)方法是使用 Scala 的 diff 函数:

def hasSameElementsUnordered[T](arrA: Array[T], arrB: Array[T]): Boolean = {
    (arrA.length == arrB.length) && (arrA diff arrB).isEmpty
}

顺便说一句,这适用于任何集合和元素类型,而不仅仅是数组和可比较对象。 在内部,diff() 构建了一个出现次数哈希映射,因此对于较大的集合,运行时行为会好得多。