1

In Scala, I'm trying to filter a map based on a unique property with the Map values.

case class Product(
  item: Item,
)
productModels: Map[Int, Product]

How can I create a new Map (or filter productModels) to only contain values where Product.Item.someproperty is unique within the Map?

I've been trying foldLeft on productModels, but can't seem to get it. I'll keep trying but want to check with you all as well.

Thanks

3
  • To make sure that I understand. As an example, replacing Product with String for simplicity, you'd expect f ( Map(1 -> "foo", 2 -> "bar", 3 -> "foo", 4 -> "bippy") to output Map(2 -> "bar", 4 -> "bippy") ) since "foo" shows up as a value for keys 1 and 3? Commented Apr 13, 2016 at 18:24
  • Thanks for question. I'd expect Map(1 -> "foo", 2 -> "bar", 4 -> "bippy") because these are the unique values Commented Apr 13, 2016 at 19:50
  • Please, can you rephrase it once again, do you need to filter your map and leave only entires that had unique product's field in original map, or you want to calculate map where there is strictly one entry for each distinct product's field value from original map (discard duplicates)? Commented Apr 14, 2016 at 1:30

4 Answers 4

3

You can do it the following way:

productModels
  .groupBy(_._1)            // produces Map[Product, Map[Int, Product]]
  .filter {case (k,v) => v.size == 1} // filters unique values
  .flatMap {case (_,v) => v}  
Sign up to request clarification or add additional context in comments.

Comments

1

The easiest way to do that is to transform your map into another map, where keys are desired fields of Item:

case class Product(item:String)

val productModels =
  Map(
    1 -> Product("a"),
    2 -> Product("b"),
    3 -> Product("c"),
    4 -> Product("a")
  )

// here I'm calculating distinct by Product.item for simplicity
productModels.map { case e@(_, v) => v.item -> e }.values.toMap

Result:

Map(4 -> Product(a), 2 -> Product(b), 3 -> Product(c))

Note, that the order of the elements is not guaranteed, as generic Map doesn't have particular order of keys. If you use Map that has item order, such as ListMap and want to preserve order of elements, here is the necessary adjustment:

productModels.toList.reverse.map { case e@(_, v) => v.item -> e }.toMap.values.toMap

Result:

res1: scala.collection.immutable.Map[Int,Product] = Map(1 -> Product(a), 3 -> Product(c), 2 -> Product(b))

4 Comments

Not clear whether OP wants to preserve only the entries that had unique values in original map or just remove entries with duplicate values, leaving exactly one entry per unique value.
I'd like one entry per unique value. If it helps, I'm trying to replicate a Ruby function uniq. So, in this case, something like nv = productModels.uniq | |pm| pm.item[:distinct_property] }
This productModels.map { case e@(_, v) => v.item -> e }.values.toMap seems to be working so far. Wow. what does the e@ do?
@ToddM it's the way to create alias for your result (or partial result) in pattern matching. For example, case (_, v) => just matched the tuple, case e@(_, v) => additionally makes the tuple available as e.
0
case class Item(property:String)
case class Product(item:Item)
val xs = Map[Int, Product]() // your example has this data structure

// just filter the map based on the item property value
xs filter { case (k,v) => v.item.property == "some property value" }

2 Comments

This assumes I know what I'm looking for in some property value, right? I'm looking for distincts
Are you saying that the map has duplicate values with different keys?
0

Here is implementation with foldLeft:

productModels.foldLeft(Map.empty[Int, Product]){ 
  (acc, el) => 
    if (acc.exists(_._2.item.someproperty == el._2.item.someproperty)) acc 
    else acc + el
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.