В то время мой подход состоял в том, чтобы иметь две функции, одна с использованием not
-оператора, а другая - простая not
-функция, принимающая предикат. Сегодня я больше не могу рекомендовать такой подход, но вместо этого предпочел бы следующее, если мне придется снова иметь дело со многими отрицаниями предикатов для ключей или значений:
inline fun <K, V> Map<out K, V>.filterKeysNot(predicate: (K) -> Boolean) = filterKeys { !predicate(it) }
inline fun <K, V> Map<out K, V>.filterValuesNot(predicate: (V) -> Boolean) = filterValues { !predicate(it) }
Таким образом, данный предикат можно просто использовать, просто вызвав filterKeysNot(givenPredicate)
, аналогично тому, что было уже возможно с filterNot
для коллекций.
Для проблемы, с которой я столкнулся в то время, я смог выполнить рефакторинг, чтобы данные могли быть надлежащим образом разделены, и поэтому отрицание предикатов больше не требовалось.
Если бы я нуждался в этом только в редких случаях, я бы предпочел придерживаться filterKeys { !predicate(it) }
или filterNot { (key, _) -> predicate(key) }
.
Следующие варианты показывают, как можно реализовать что-то вроде Predicates.not
или Predicate.negate
:
Следующее позволит использовать оператор !
для отрицания предиката (если необходимо разрешить несколько параметров, требуется соответствующая перегрузка):
operator fun <T> ((T) -> Boolean).not() = { e : T -> !this(e) }
Следующий позволяет использовать not( { /* a predicate */ } )
. Это, однако, по крайней мере для меня, на самом деле не более читабельно:
inline fun <T> not(crossinline predicate: (T) -> Boolean) = { e : T -> !predicate(e)}
Обычаи:
val matchingHello : (String) -> Boolean = { it == "hello" }
mapOf("hello" to "world", "hi" to "everyone")
.filterKeys(!matchingHello)
// or .filterKeys(not(matchingHello))
// or .filterKeys(matchingHello.not())
// or as shown above:
// .filterKeysNot(matchingHello)
.forEach(::println)