سؤال

بدءًا من قائمة الكائنات التي تحتوي على معلمتين اعتبارية وعملة، كيف يمكنني تجميع الإجمالي الافتراضي لكل عملة؟

منح:

case class Trade(name: String, amount: Int, currency: String)

val trades = List(
  Trade("T150310", 10000000, "GBP"),
  Trade("T150311", 10000000, "JPY"),
  Trade("T150312", 10000000, "USD"),
  Trade("T150313", 100, "JPY"),
  Trade("T150314", 1000, "GBP"),
  Trade("T150315", 10000, "USD")
)

كيف يمكنني الحصول على:

Map(JPY -> 10000100, USD -> 10010000, GBP -> 10001000)
هل كانت مفيدة؟

المحلول

ولقد كتبت مجموعة من قبل عملية بسيطة (في الواقع Groupable trait مع التحويل الضمني من Iterable) التي من شأنها أن تسمح لك لمجموعة الصفقات عن طريق currency بها:

trait Groupable[V] extends Iterable[V] {
  def groupBy(f: V => K): MultiMap[K, V] = {
    val m = new mutable.HashMap[K, Set[V]] with mutable.MultiMap[K, V]
    foreach { v => m add (f(v), v) } //add is defined in MultiMap
    m
  }
}
implicit def it2groupable(it: Iterable[V]): Groupable[V] = new Groupable[V] {
  def elements = it.elements
}

وهكذا Groupable هو مجرد توفير وسيلة لانتزاع <م> مفتاح من كل عنصر في Iterable ثم تجمع كل هذه العناصر التي لديها نفس المفتاح. لذلك، في الحالة الخاصة بك:

//mm is a MultiMap[Currency, Trade]
val mm = trades groupBy { _.currency } 

ويمكنك الآن القيام mapElements بسيطة جدا (mm هو Map) وfoldLeft (أو /: - تستحق فهم المشغل foldLeft، لأنها تتيح تجمعات موجزة للغاية على المجموعات) للحصول على مبلغ:

val sums: Map[Currency, Int] = mm mapElements { ts => 
    (0 /: ts) { (sum,t) => sum + t.notional } 
}

والاعتذار إذا كنت قد ارتكبت بعض الاخطاء في هذا السطر الأخير. ts هي قيم mm، والتي هي (طبعا) Iterable[Trade].

نصائح أخرى

إذا كنت تستخدم جذع آلية بالفعل هناك. ويعرف groupBy على عبورها ومبلغ يمكن تطبيقها مباشرة إلى القائمة، لم يكن لديك لكتابة أضعاف.

scala> trades groupBy (_.currency) map { case (k,v) => k -> (v map (_.amount) sum) }
res1: Iterable[(String, Int)] = List((GBP,10001000), (JPY,10000100), (USD,10010000))

ابتداء Scala 2.13, ، يتم توفير معظم المجموعات مع groupMapReduce الطريقة التي (كما يوحي اسمها) تعادل (أكثر كفاءة) لـ a groupBy تليها mapValues وخطوة تقليل:

trades.groupMapReduce(_.currency)(_.amount)(_ + _)
// immutable.Map[String,Int] = Map(JPY -> 10000100, USD -> 10010000, GBP -> 10001000)

هذا:

  • groupعناصر s بناءً على عملتها (جزء المجموعة من مجموعةمابريديوس)

  • mapالقيم المجمعة إلى مقدارها (خريطة جزء من المجموعةخريطةيقلل)

  • reduceقيم ق (_ + _) عن طريق جمعها (تقليل جزء من groupMapيقلل).

هذه نسخة مكافئة يتم تنفيذها في تمريرة واحدة من خلال قائمة:

trades.groupBy(_.currency).mapValues(_.map(_.amount).reduce(_+_))
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top