Нахождение среднего значения параметра, связанного с объектом в списке - PullRequest
0 голосов
/ 30 сентября 2019

Я новичок в Scala, и я боролся с Option и списками. У меня есть следующий объект:

object Person {

    case class Person(fName: String,
                      lName: String,
                      jeans: Option[Jeans],
                      jacket: Option[Jacket],
                      location: List[Locations],
                      age: Int)

    case class Jeans(brand: String, price: Int, color: String)
...
}

И я пытаюсь написать функцию, которая принимает в качестве входного списка тип человека и возвращает среднюю цену их джинсов:

def avgPriceJeans(input: List[Person]): Int

Ответы [ 3 ]

1 голос
/ 30 сентября 2019

Как указывает @SethTissue, это относительно просто:

val prices = persons.flatMap(_.jeans.map(_.price))
prices.sum.toDouble / prices.length

В первой строке нужно выделить несколько символов:

Взяв это изнутри, выражение jeans.map(_.price) беретзначение jeans, которое равно Option[Jeans], и извлекает поле price, чтобы получить Option[Int]. Вызов flatMap эквивалентен map, за которым следует flatten. Вызов map применяет это внутреннее выражение к полю jeans каждого элемента persons. Это превращает List[Person] в List[Option[Int]]. Вызов flatten извлекает все значения Int из Some[Int] и отбрасывает все значения None. Это дает List[Int] с одним элементом для каждого Person с непустым полем jeans.

Вторая строка просто суммирует значения в List, преобразует его в Double изатем делится на длину списка. Добавление проверки ошибок оставлено в качестве упражнения!

1 голос
/ 30 сентября 2019

Если у вас есть список значений и вы хотите свести все их к одному значению, применяя какую-то операцию. Вам нужен fold, наиболее распространенным из них будет foldLeft.

Как вы можете видеть в scaladoc . Этот метод получает начальное значение и комбинированную функцию.
Должно быть очевидно, что начальное значение должно быть zero. И что комбинационная функция должна взять текущее накопление и добавить к нему цену текущего джинса.

Тем не менее, теперь у нас есть другая проблема, джинсы могут существовать или не существовать, поэтому мы используем опцию . В этом случае нам нужен способ сказать, если они существуют, дать мне их цену, если не дать значение по умолчанию (которое в этом случае имеет смысл быть другим zero) .
И этоименно то, что Option.fold дает нам.

Таким образом, мы заканчиваем чем-то вроде:

val sum = input.foldLeft(0) {
  (acc, person) => 
    acc + person.jeans.fold(ifEmpty = 0)(_.price)
}

Теперь, когда вам нужно среднее значение, вам нужно только разделить эту суммусо счетом.

Однако мы можем сделать счет в том же foldLeft, просто чтобы избежать дополнительной итерации.
(Я изменил тип возвращаемого значения, а также price свойство, до Double для обеспечения точных результатов) .

def avgPriceJeans(input: List[Person]): Double = {
  val (sum, count) = input.foldLeft((0.0d, 0)) {
    case ((accSum, accCount), person) =>
      (
        accSum   + person.jeans.fold(ifEmpty = 0.0d)(_.price),
        accCount + 1
      )
  }
  sum / count
}
0 голосов
/ 11 октября 2019

Один из подходов заключается в вычислении значения цены джинсов для каждого элемента списка. После этого вы можете суммировать все значения (с помощью метода sum) и делить на размер списка. Я справился с ситуацией, когда джинсы имеют значение None с нулевым значением цены (поэтому я считаю это суммой).

Вот код:

def avgPriceJeans(input: List[Person]): Int =
    input.map(_.jeans.map(_.price).getOrElse(0)).sum / input.size
...