تجميع قيم القائمة في Scala
-
06-07-2019 - |
سؤال
بدءًا من قائمة الكائنات التي تحتوي على معلمتين اعتبارية وعملة، كيف يمكنني تجميع الإجمالي الافتراضي لكل عملة؟
منح:
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(_+_))