Scala - 找到两个Seq不同的第一个位置

时间:2015-05-28 17:28:22

标签: scala diff sequence seq

Scala带有漂亮的corresponds方法:

val a = scala.io.Source.fromFile("fileA").getLines().toSeq()
val b = scala.io.Source.fromFile("fileB").getLines().toSeq()

val areEqual = a.corresponds(b){_.equals(_)}

if(areEqual) ...

我非常喜欢这种简洁。

是否已经定义了类似的方法,它还会向我报告两个序列不同的第一个位置?

即。是否有更惯用的方式来写这样的东西:

val result = ((seqA zip seqB).zipWithIndex).find{case ((a,b),i) => !a.equals(b)} match{
    case Some(((a,b),i)) => s"seqA and seqB differ in pos $i: $a <> $b"
    case _ => "no difference"
}

因为你可以看到,这是颈部的血腥疼痛。如果我想使用三元组而不是元组元组,那就更糟了:

val result = (((seqA zip seqB).zipWithIndex) map {case (t,i) => (t._1,t._2,i)}).find{case (a,b,i) => !a.equals(b)} match{
    case Some((a,b,i)) => s"seqA and seqB differ in pos $i: $a <> $b"
    case _ => "no difference"
}

我知道diff方法。不幸的是,那个人无视元素的顺序。

2 个答案:

答案 0 :(得分:8)

您可以使用indexWhere(请参阅ScalaDoc),如下所示:

(as zip bs).indexWhere{case (x,y) => x != y}

示例:

scala> val as = List(1,2,3,4)
scala> val bs = List(1,2,4,4)

scala> (as zip bs).indexWhere{case (x,y) => x != y}

res0: Int = 2

但是,请注意,如果一个Seq比另一个更长(zip截断更长的Seq),那么基于zip的所有解决方案都可能没有报告差异 - 这可能是您可能需要的,也可能不是。 ..

更新:对于长度相等的Seq,其他方法如下:

as.indices.find(i => as(i) != bs(i))

这很好,因为它返回Option[Int],所以如果Seqs之间没有差异,它会返回None而不是魔法-1。

如果asbs短,则其行为与其他解决方案相同,但如果as更长则失败(当然,您可以采用最小长度)。

但是,因为它通过索引处理两个Seqs,所以它只能在IndexedSeq s表现良好。

更新2 :我们可以使用lift处理不同的Seq长度,以便在按索引检索元素时获得选项:

bs.indices.find(i => as.lift(i) != bs.lift(i))

所以,如果as = [1,2]bs = [1,2,3],它们不同的第一个索引是2(因为as中缺少此元素)。但是,在这种情况下,我们需要在最长的Seq上调用indices而不是最短的 - 或使用max明确检查哪个是最长的,例如

(0 until (as.length max bs.length)).find(i => as.lift(i) != bs.lift(i))

答案 1 :(得分:3)

这好一点:

$actual = iconv("UTF-8", "ISO-8859-1//TRANSLIT", $actual);

请参阅:

(as zip bs).zipWithIndex.collectFirst { case ((a,b),i) if a!=b => i }

如果您希望输出中有def firstDiff[A,B](as: Seq[A], bs: Seq[B]) = (as zip bs).zipWithIndex.collectFirst { case ((a,b),i) if a!=b => i } firstDiff(Seq(1,2,3,4), Seq(1,2,9,4)) // res1: Option[Int] = Some(2) a

b

另外:如果您希望它像(as zip bs).zipWithIndex.collectFirst { case ((a,b),i) if a!=b => (i,a,b) } 示例一样,您可以将其作为扩展方法:

corresponds

甚至:

implicit class Enriched_counts_TraversableOnce[A](val as: TraversableOnce[A]) extends AnyVal {
  def firstDiff[B](bs: TraversableOnce[B]): Option[Int] = {
    (as.toIterator zip bs.toIterator)
      .zipWithIndex
      .collectFirst { case ((a,b),i) if a!=b => i }
  }
}

Seq(1,2,3,4).firstDiff(Seq(1,2,9,4))
// res2: Option[Int] = Some(2)