У меня есть карта, состоящая из строк и списков кортежей:
Map[String, List[(String, Int]]
Некоторые примеры линий на карте выглядят так:
"test1", List(("apple", 0), ("pear", 1), ("banana", 0))
"test2", List(("green", 1), ("yellow", 1), ("red", 0))
"test3", List(("monday", 0), ("tuesday", 1), ("wednesday", 0))
каков наилучший способ извлечь список первых строк в каждой строке, только если список кортежей содержит более 1 значения, равного 0?
Другими словами, мне нужен список, содержащий «test1» и «test2», поскольку эти 2 строки являются единственными строками, которые содержат более 1 «0» в своем списке кортежей.
Но «test2» имеет больше 1, чем 0, поэтому, может быть, вместо этого следует напечатать test3?
Ваша отправная точка будет либо collect
, либо filter
; оба метода полезны, когда у вас есть коллекция и вы хотите создать новую коллекцию с некоторыми условными условиями.
Ваше условие, как описано, звучит как прецедент для метода count
, который определен практически для каждого типа коллекции в стандартной библиотеке.
Это должно сработать...
theMapFromYourExample
.collect { case (key, list) if list.count(_._2 == 0) > 1 => key }
.toList
... но есть некоторые соображения:
.collect
и toList
создадут новые целые коллекции. Объединение их вместе означает, что вы тратите немного памяти и процессорного времени на создание промежуточной коллекции, которая просто отправляется прямо в сборку мусора, как только ваше выражение завершено. Чтобы избежать выделения памяти для всей промежуточной коллекции, вы можете использовать шаблон Вид, доступный через метод .view
в большинстве коллекций..count
равен O(N) размеру списка, так как ему нужно проверить каждый элемент в списке, чтобы увидеть, соответствует ли он вашему условному выражению. Поскольку вам нужно только, чтобы счет был «по крайней мере» определенного значения, вы можете прекратить считать раньше, если вы превысите 1. Метод lengthCompare
полезен для такого рода вещей.Итак, вот примерно тот же код, использующий «представления» для повышения эффективности:
def hasAtLeastTwoZeroes(list: List[(String, Int)]) = {
list
.view // lets us call `.filter` without allocating a new List
.filter(_._2 == 0) // apply conditional: value is 0
.lengthCompare(1) > 0 // like saying `.length > 1`, but is O(1), not O(N)
}
theMapFromYourExample
.view // lets us call `collect` without allocating an intermediate collection
.collect { case (key, list) if hasAtLeastTwoZeroes(list) => key }
.toList // builds a List from the collected view
Обновлять, как отметил Луис в комментариях, в Scala 2.13 добавлен метод sizeIs
, который имеет более удобный интерфейс, чем lengthCompare
Вы можете использовать sizeIs > 1
вместо lengthCompare(1) > 0
, и он все равно остановится раньше.
@LuisMiguelMejíaSuárez, очень круто. Похоже на функции Scala 2.13, о которых я не знал (я все еще застрял на 2.12 по работе).
Что вы пробовали? Почему это не сработало? - Подсказки: вы хотите
collect
+exists