Kotlin: Find Count из вложенного набора в List (более функциональный подход) - PullRequest
0 голосов
/ 12 января 2019

Функция ниже создает карту, получает количество пассажиров, где пассажиры находятся> minTrips. Код работает совершенно нормально. Пожалуйста, смотрите ниже

fun List<Trip>.filter(minTrips : Int): Set<Passenger> {
    var passengerMap: HashMap<Passenger, Int> = HashMap()

    this.forEach { it: Trip ->
        it.passengers.forEach { it: Passenger ->
            var count: Int? = passengerMap.get(it)
            if (count == null) {
                count = 1
                passengerMap.put(it, count)
            } else {
                count += 1
                passengerMap.put(it, count)
            }
        }
    }

    val filteredMinTrips: Map<Passenger, Int> = passengerMap.filterValues { it >= minTrips }
    println (" Filter Results = ${filteredMinTrips}")
    return filteredMinTrips.keys
}

Хотя это написано на Kotlin, похоже, что код сначала был написан на Java, а затем преобразован в Kotlin. Если бы это было действительно написано на Kotlin, я уверен, что это не было бы так много строк кода. Как я могу уменьшить строки кода? Каков был бы более функциональный подход для решения этой проблемы? Какие функции или функции я могу использовать для извлечения набора пассажиров, где пассажиры находятся> minTrips? Это слишком много кода и кажется сумасшедшим. Любые указатели будут полезны здесь.

Ответы [ 2 ]

0 голосов
/ 12 января 2019

Один из способов сделать это - воспользоваться плоской картой Котлина и групповыми вызовами. Создав список всех пассажиров во всех поездках, вы можете сгруппировать их, сосчитать и вернуть тех, у кого более определенного числа.

Предполагается, что у вас есть такие классы данных (только основные данные):

data class Passenger(val id: Int)
data class Trip(val passengers: List<Passenger>)

Я смог написать это:

fun List<Trip>.frequentPassengers(minTrips: Int): Set<Passenger> =
    this
       .flatMap { it.passengers }
       .groupingBy { it }
       .eachCount()
       .filterValues { it >= minTrips }
       .keys

Это хорошо, потому что это одно выражение. Проходя через это, мы смотрим на каждый Trip и извлекаем все его Passengers. Если бы мы только что сделали map здесь, у нас было бы List<List<Passenger>>, но мы хотим List<Passenger>, поэтому мы планируем это для достижения цели. Затем мы groupBy сами Passenger объекты и вызываем eachCount() для возвращенного объекта, давая нам Map<Passenger, Int>. Наконец, мы отфильтровываем карту по интересующим нас Пассажирам и возвращаем набор ключей.

Обратите внимание, что я переименовал вашу функцию, List уже имеет filter на ней, и хотя подписи отличаются, я нашел это сбивающим с толку.

0 голосов
/ 12 января 2019

В основном вы хотите подсчитать количество рейсов для каждого пассажира, поэтому вы можете поместить всех пассажиров в список, а затем сгруппировать их по ним, а затем подсчитать количество происшествий в каждой группе:

fun List<Trip>.usualPassengers(minTrips : Int) = // 1
        flatMap(Trip::passengers) // 2
        .groupingBy { it } // 3
        .eachCount() // 4
        .filterValues { it >= minTrips } // 5
        .keys // 6

Пояснение:

  1. тип возврата Set<Passenger> можно сделать вывод
  2. this может быть опущено, возвращается список вида [p1, p2, p1, p5, ...]
  3. a Создана группировка , которая выглядит следующим образом [p1=[p1, p1], p2=[p2], ...]]
  4. количество случаев в каждой группе будет подсчитано: [p1=2, p2=1, ...]
  5. все элементы со значениями, которые будут отфильтрованы меньше minTrips
  6. все оставленные ключи будут возвращены [p1, p2, ...]

p1...pn являются экземплярами Passenger

...