22

Map[String, String]Scalaに があると想像してください。

マップ内のキーと値のペアの完全なセットと照合したいと考えています。

このようなことが可能になるはずです

val record = Map("amenity" -> "restaurant", "cuisine" -> "chinese", "name" -> "Golden Palace")
record match {
    case Map("amenity" -> "restaurant", "cuisine" -> "chinese") => "a Chinese restaurant"
    case Map("amenity" -> "restaurant", "cuisine" -> "italian") => "an Italian restaurant"
    case Map("amenity" -> "restaurant") => "some other restaurant"
    case _ => "something else entirely"
}

コンパイラはthulsyに文句を言います:

error: value Map is not a case class constructor, nor does it have an unapply/unapplySeq method

でのキーと値の組み合わせのパターン マッチの現在の最良の方法は何Mapですか?

4

6 に答える 6

11

flatMap興味のある値を引き出して、それらと照合するために使用できます。

List("amenity","cuisine") flatMap ( record get _ ) match {
  case "restaurant"::"chinese"::_ => "a Chinese restaurant"
  case "restaurant"::"italian"::_ => "an Italian restaurant"
  case "restaurant"::_            => "some other restaurant"
  case _                          => "something else entirely"
}

このスニペットページの#1を参照してください。

キーの任意のリストに次のような特定の値があるかどうかを確認できます。

if ( ( keys flatMap ( record get _ ) ) == values ) ...

上記は、キーがマップに存在しない場合でも機能することに注意してください。ただし、キーがいくつかの値を共有している場合は、値のリストで/mapの代わりに使用flatMapし、明示的に使用することをお勧めします。たとえば、この場合、「アメニティ」がなく、「料理」の値が「レストラン」である可能性がある場合(この例ではばかげていますが、別のコンテキストではおそらくそうではありません)、あいまいになります。SomeNonecase "restaurant"::_

また、後者はこれら2つの要素の後に要素がないことを不必要にチェックするため、これcase "restaurant"::"chinese"::_よりもわずかに効率的であることに注意してください。case List("restaurant","chinese")

于 2012-11-24T11:01:02.090 に答える
7

問題の値を検索し、それらをタプルに貼り付け、それに対してパターン マッチを行うことができます。

val record = Map("amenity" -> "restaurant", "cuisine" -> "chinese", "name" -> "Golden Palace")
(record.get("amenity"), record.get("cuisine")) match {
    case (Some("restaurant"), Some("chinese")) => "a Chinese restaurant"
    case (Some("restaurant"), Some("italian")) => "an Italian restaurant"
    case (Some("restaurant"), _) => "some other restaurant"
    case _ => "something else entirely"
}

または、ネストされた一致をいくつか行うこともできます。これは少しきれいになるかもしれません:

val record = Map("amenity" -> "restaurant", "cuisine" -> "chinese", "name" -> "Golden Palace")
record.get("amenity") match {
  case Some("restaurant") => record.get("cuisine") match {
    case Some("chinese") => "a Chinese restaurant"
    case Some("italian") => "an Italian restaurant"
    case _ => "some other restaurant"
  }
  case _ => "something else entirely"
}

map.get(key)を返すことに注意してくださいOption[ValueType](この場合、ValueType は String になります)。そのためNone、キーがマップに存在しない場合は、例外をスローするのではなく、返されます。

于 2012-11-23T23:06:06.213 に答える
6

パターンマッチングはあなたが望むものではありません。AにBが完全に含まれているかどうかを調べたい

val record = Map("amenity" -> "restaurant", "cuisine" -> "chinese", "name" -> "Golden Palace")
val expect = Map("amenity" -> "restaurant", "cuisine" -> "chinese")
expect.keys.forall( key => expect( key ) == record( key ) )

編集:一致基準の追加

このようにして、一致基準を簡単に追加できます

val record = Map("amenity" -> "restaurant", "cuisine" -> "chinese", "name" -> "Golden Palace")

case class FoodMatcher( kv: Map[String,String], output: String )

val matchers = List( 
    FoodMatcher(  Map("amenity" -> "restaurant", "cuisine" -> "chinese"), "chinese restaurant, che che" ),
    FoodMatcher(  Map("amenity" -> "restaurant", "cuisine" -> "italian"), "italian restaurant, mama mia" )
)

for {
    matcher <- matchers if matcher.kv.keys.forall( key => matcher.kv( key ) == record( key ) )
} yield matcher.output

与えます:

List(chinese restaurant, che che)

于 2012-11-24T02:56:48.833 に答える
2

他のすべての回答が非常に賢明であることに同意したにもかかわらず、実際にマップを使用してパターン マッチングする方法があるかどうかを確認することに興味があったため、以下をまとめました。上位の回答と同じロジックを使用して、一致を判断します。

class MapSubsetMatcher[Key, Value](matcher: Map[Key, Value]) {
  def unapply(arg: Map[Key, Value]): Option[Map[Key, Value]] = {
    if (matcher.keys.forall(
      key => arg.contains(key) && matcher(key) == arg(key)
    ))
      Some(arg)
    else
      None
  }
}

val chineseRestaurant = new MapSubsetMatcher(Map("amenity" -> "restaurant", "cuisine" -> "chinese"))
val italianRestaurant = new MapSubsetMatcher(Map("amenity" -> "restaurant", "cuisine" -> "italian"))
val greatPizza = new MapSubsetMatcher(Map("pizza_rating" -> "excellent"))

val record = Map("amenity" -> "restaurant", "cuisine" -> "chinese", "name" -> "Golden Palace")
val frankies = Map("amenity" -> "restaurant", "cuisine" -> "italian", "name" -> "Frankie's", "pizza_rating" -> "excellent")


def matcher(x: Any): String = x match {
  case greatPizza(_) => "It's really good, you should go there."
  case chineseRestaurant(matchedMap) => "a Chinese restaurant called " +
    matchedMap.getOrElse("name", "INSERT NAME HERE")
  case italianRestaurant(_) => "an Italian restaurant"
  case _ => "something else entirely"
}

matcher(record)
// a Chinese restaurant called Golden Palace
matcher(frankies)
// It's really good, you should go there.
于 2017-01-11T22:39:38.900 に答える
2

抽出するキーを指定する必要があり、値を照合できる別のバージョンは次のとおりです。

class MapIncluding[K](ks: K*) {
  def unapplySeq[V](m: Map[K, V]): Option[Seq[V]] = if (ks.forall(m.contains)) Some(ks.map(m)) else None
}

val MapIncludingABC = new MapIncluding("a", "b", "c")
val MapIncludingAAndB = new MapIncluding("a", "b")

Map("a" -> 1, "b" -> 2) match {
  case MapIncludingABC(a, b, c) => println("Should not happen")
  case MapIncludingAAndB(1, b) => println(s"Value of b inside map is $b")
}
于 2017-08-08T13:20:55.043 に答える