4

私はこのようなリストを持っています:

val l= List(("Agent", "PASS"), ("Agent", "FAIL"), ("Agent 1", "FAIL"), ("Agent", "PASS"), ("Agent 2", "PASS") )

そして私はこのようなリストで終わる必要があります:

val filteredList= List(("Agent", "FAIL"), ("Agent 1", "FAIL"), ("Agent 2", "PASS") )

どうしたの?

("Agent", "PASS"), ("Agent", "FAIL")

になります

("Agent", "FAIL")

(少なくとも1つのFAILがある場合、そのエントリを保持する必要があるため)

エージェント1とエージェント2のエントリは、それぞれに1つのエントリしかないため、同じままです。

私が見つけた最も近い答えは 、Scalaでリスト内の一意のアイテムを見つける方法です が、FAILでエントリを保持する方法がわかりません。

質問が明確であることを願っています。そうでない場合は、より良い例を示します。

ありがとう

4

8 に答える 8

9

前文

ステータスが優先度を持っていると見なされる可能性があることに気付きました。一連の(agent,status)ペアが与えられた場合、タスクは各エージェントの最も優先度の高いステータスのみを選択することです。残念ながら、ステータスはそのように定義された明示的な順序で強く型付けされていませんが、値が2つしかない文字列であるため、優先度に1:1で対応する文字列の順序を安全に使用できます。

私の答えは両方とも、2つの有用な事実を利用しています。

  1. 自然な文字列の順序では、、、"FAIL" < "PASS"そう:

    List("PASS", "FAIL", "PASS").sorted.head = "FAIL"
    
  2. 2つのタプルの 場合(x,a)、および(x,b)(x,a) > (x, b)(a > b)

更新された返信

val solution = l.sorted.reverse.toMap

メソッドを介してaSeq[(A,B)]をaに変換する場合、タプルの元のシーケンスの各「キー」は、結果のマップに1回だけ表示されます。たまたま、変換はそのような最後の発生を使用します。Map[A,B].toMap

l.sorted.reverse = List(
  (Agent 2,PASS),  // <-- Last "Agent 2"
  (Agent 1,FAIL),  // <-- Last "Agent 1"
  (Agent,PASS),
  (Agent,PASS),
  (Agent,FAIL))    // <-- Last "Agent"

l.sorted.reverse.toMap = Map(
  Agent 2 -> PASS,
  Agent 1 -> FAIL,
  Agent -> FAIL)

元の返信

答えから始めて...

val oldSolution = (l groupBy (_._1)) mapValues {_.sorted.head._2}

...そして私の作業を示しています:)

//group
l groupBy (_._1) = Map(
  Agent 2 -> List((Agent 2,PASS)),
  Agent 1 -> List((Agent 1,FAIL)),
  Agent -> List((Agent,PASS), (Agent,FAIL), (Agent,PASS))
)

//extract values
(l groupBy (_._1)) mapValues {_.map(_._2)} = Map(
  Agent 2 -> List(PASS),
  Agent 1 -> List(FAIL),
  Agent -> List(PASS, FAIL, PASS))

//sort
(l groupBy (_._1)) mapValues {_.map(_._2).sorted} = Map(
  Agent 2 -> List(PASS),
  Agent 1 -> List(FAIL),
  Agent -> List(FAIL, PASS, PASS))

//head
(l groupBy (_._1)) mapValues {_.map(_._2).sorted.head} = Map(
  Agent 2 -> PASS,
  Agent 1 -> FAIL,
  Agent -> FAIL)

agent -> statusただし、最初に抽出しなくても、ペアを直接並べ替えることができます_2

//group & sort
(l groupBy (_._1)) mapValues {_.sorted} = Map(
  Agent 2 -> List((Agent 2,PASS)),
  Agent 1 -> List((Agent 1,FAIL)),
  Agent -> List((Agent,FAIL), (Agent,PASS), (Agent,PASS)))

//extract values
(l groupBy (_._1)) mapValues {_.sorted.head._2} = Map(
  Agent 2 -> PASS,
  Agent 1 -> FAIL,
  Agent -> FAIL)

いずれの場合も、必要に応じて、ペアのリストに自由に変換して戻すことができます。

l.sorted.reverse.toMap.toList = List(
  (Agent 2, PASS),
  (Agent 1, FAIL),
  (Agent, FAIL))
于 2010-12-15T10:20:04.223 に答える
6

これは、あなたの望むことですか?

jem@Respect:~$ scala
Welcome to Scala version 2.8.0.final (Java HotSpot(TM) Client VM, Java 1.6.0_21).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val l= List(("Agent", "PASS"), ("Agent", "FAIL"), ("Agent 1", "FAIL"), ("Agent", "PASS"), ("Agent 2", "PASS") )
l: List[(java.lang.String, java.lang.String)] = List((Agent,PASS), (Agent,FAIL), (Agent 1,FAIL), (Agent,PASS), (Agent 2,PASS))

scala> l.foldLeft(Map.empty[String, String]){(map,next) =>
     |   val (agent, result) = next
     |   if ("FAIL" == result) map.updated(agent, result)
     |   else {           
     |     val maybeExistingResult = map.get(agent)
     |     if (maybeExistingResult.map(_ == "FAIL").getOrElse(false)) map
     |     else map.updated(agent, result)
     |   }
     | }
res0: scala.collection.immutable.Map[String,String] = Map((Agent,FAIL), (Agent 1,FAIL), (Agent 2,PASS))

scala> res0.toList
res1: List[(String, String)] = List((Agent 2,PASS), (Agent 1,FAIL), (Agent,FAIL))

または、これはより短く、よりあいまいな解決策です。

scala> l.groupBy(_._1).map(pair => (pair._1, pair._2.reduceLeft((a,b) => if ("FAIL" == a._2 || "FAIL" == b._2) (a._1, "FAIL") else a))).map(_._2).toList
res2: List[(java.lang.String, java.lang.String)] = List((Agent 2,PASS), (Agent 1,FAIL), (Agent,FAIL))
于 2010-12-15T06:04:39.470 に答える
4

良い解決策はたくさんありますが、とにかくここに私のものがあります。:-)

l
.groupBy(_._1) // group by key
.map { 
    case (key, list) => 
        if (list.exists(_._2 == "FAIL")) (key, "FAIL") 
        else (key, "PASS")
}

これが私が突然のエピファニーでちょうど持っていたもう一つです:

def booleanToString(b: Boolean) = if (b) "PASS" else "FAIL"
l
.groupBy(_._1)
.map {
    case (key, list) => key -> booleanToString(list.forall(_._2 == "PASS"))
}
于 2010-12-15T15:18:45.357 に答える
2

これが私の見解です。最初の機能的な解決策:

l.map(_._1).toSet.map({n:String=>(n, if(l contains (n,"FAIL")) "FAIL" else "PASS")})

最初に名前を一意に分離し(toSet)、次に各名前をタプルにマップします。タプルはそれ自体を最初の要素"FAIL"として、失敗がに含まれている場合は2番目の要素としてl、そうでない場合は明らかに。でなければなりません"PASS"

結果はセットです。もちろんtoList、本当にリストが必要な場合は、コールチェーンの最後で行うことができます。

これが必須の解決策です:

var l = List(("Agent", "PASS"), ("Agent", "FAIL"), ("Agent 1", "FAIL"), ("Agent", "PASS"), ("Agent 2", "PASS"))
l.foreach(t=>if(t._2=="FAIL") l=l.filterNot(_ == (t._1,"PASS")))
l=l.toSet.toList

必須なのであまり好きではありませんがねえ。ある意味では、これを手作業で解決したときに実際に行うことをよりよく反映しています。表示されるそれぞれ"FAIL"について、対応するすべての"PASS"esを削除します。その後、一意性を確保します(.toSet.toList)。

これは、再割り当てされるために必要な命令型ソリューションであることにl注意してください。var

于 2010-12-15T07:03:40.090 に答える
1

Scalaの集計リスト値を見てください

あなたの場合、エージェントごとにグループ化し、PASS + PASS=>PASSとANY+FAIL=>FAILを折りたたんで集計します。

于 2010-12-15T05:42:47.360 に答える
1

おそらく、最初にグループ化してから、PASS/FAILの区別を見つける方が効率的です。

l.filter(_._2 == "PASS").toSet -- l.filter(_._2 == "FAIL").map(x => (x._1, "PASS"))

これはの出力に基づいています("Agent", "PASS")が、エージェントが必要な場合は次のようになります。

l.filter(_._2 == "PASS").map(x => x._1).toSet -- l.filter(_._2 == "FAIL").map(x => x._1)

どういうわけか私はその2番目のものが短くなることを期待しました。

于 2010-12-15T08:09:19.180 に答える
1

だから私が理解しているように、あなたは次のことをしたいのです:

  1. タプルを最初のエントリ(「キー」)でグループ化します
  2. キーごとに、すべてのタプルの2番目のエントリで値「FAIL」を確認します
  3. 「FAIL」が見つかった場合は(key、 "FAIL")を生成し、それ以外の場合は(key、 "PASS")を生成します

、、などはまだ読みにくいと思うのでfoldLeftreduceLeft理解のために上記の手順を直接翻訳します。

scala> for ((key, keyValues) <- l.groupBy{case (key, value) => key}) yield {
     |   val hasFail = keyValues.exists{case (key, value) => value == "FAIL"}
     |   (key, if (hasFail) "FAIL" else "PASS")                              
     | }
res0: scala.collection.immutable.Map[java.lang.String,java.lang.String] = Map((Agent 2,PASS), (Agent 1,FAIL), (Agent,FAIL))

.toList本当に必要な場合は、最後に電話をかけることができますList

編集:ダニエルC.ソブラルによって提案されexistsたイディオムを使用するようにわずかに変更されました。

于 2010-12-15T10:22:04.953 に答える
0

元の注文を保持する必要がありますか?そうでない場合、私が知っている最短の解決策(これも非常に簡単です)は次のとおりです。

{
  val fail = l.filter(_._2 == "FAIL").toMap        // Find all the fails
  l.filter(x => !fail.contains(x._1)) ::: fail.toList // All nonfails, plus the fails
}

ただし、これによって余分なパスが削除されることはありません。それが必要な場合は、追加のマップが必要です。

{
  val fail = l.filter(_._2 == "FAIL").toMap
  l.toMap.filter(x => !fail.contains(x._1)).toList ::: fail.toList
}

一方、最初に見つけたのと同じ順序で要素を取得することもできます。最初の興味深いアイテムがいつ出現したかを追跡する必要があるため、これは注意が必要です。

{
  val fail = l.filter(_._2 == "FAIL").toMap
  val taken = new scala.collection.mutable.HashMap[String,String]
  val good = (List[Boolean]() /: l)((b,x) => {
    val okay = (!taken.contains(x._1) && (!fail.contains(x._1) || x._2=="FAIL"))
    if (okay) taken += x
    okay :: b
  }).reverse
  (l zip good).collect{ case (x,true) => x }
}
于 2010-12-15T17:27:27.877 に答える