Kotlin: groupBy and convert to other object - kotlin

I have a list of objects
data class OldFormat(ShiftId: Int, NozzleValue: Int, NozzleId: Int , UserId: Int)
which I want to group by two of fields "shiftId and userId" and then subtract the maximum value of each group from the minimum value of the same group and then
sum the result and then convert it to a new object with this class:
data class NewFormat(ShiftId: Int, NozzleValue: Int, UserId: Int)
The process would be like this:
listOfOldFormat -> groupby(shiftId, userId) -> sum(maximumValue-minimumValue) -> listOfNewFormat

We do have groupBy function, so we have everything we need to.
I am not sure what do you mean by subtract the maximum value of each group from the minimum value of the same group and then sum the result (what should I sum?), so I did it as group.value.max - group.value.min and it's nozzeValue for NewFormat.
Code snippet:
data class OldFormat(val shiftId: Int, val nozzleValue: Int, val nozzleId: Int, val userId: Int)
data class NewFormat(val shiftId: Int, val nozzleValue: Int, val userId: Int)
fun main() {
val old = listOf(
OldFormat(0, 10, 10, 0),
OldFormat(0, 120, 10, 1),
OldFormat(1, 11, 8, 10),
OldFormat(0, 10, 1, 1),
OldFormat(1, 50, 10, 10)
) // Example data
old.groupBy {
it.shiftId to it.userId // After it we have Map<Key, List<OldFormat>>, where key is pair of shiftId and userId
}.map { entry ->
val max = entry.value.maxBy { it.nozzleValue }?.nozzleValue ?: 0
val min = entry.value.minBy { it.nozzleValue }?.nozzleValue ?: 0
entry.key to (max - min) // Just do whatever you want with that data
}.map {
NewFormat(
shiftId = it.first.first,
userId = it.first.second,
nozzleValue = it.second
) // And now map it into type you want
}.let {
println(it) // Just for seeing result
}
}

Related

How to combine fields of objects by a certain attribute?

I need to filter these Person objects by name, then count the number of Devices for each unique name.
data class Device(val name: String, val price: Int)
data class Person(val name: String, val age: Int, val devices: List<Device>)
data class PersonFinal(val name: String, val age: Int, val count: Int)
val person1 = Person("Jack", 20, listOf(Device("Samsung", 1500), Device("iPhone", 2500)))
val person2 = Person("Jack", 20, listOf(Device("Samsung", 3500), Device("iPhone", 5500)))
val person3 = Person("John", 20, emptyList())
The final result should be:
// [PersonFinal(name=Jack, age=20, count=4), PersonFinal(name=John, age=20, count=0)]
How can I do this using Kotlin?
If someone needed, my final solution looks like:
listOf(person1, person2, person3)
.groupBy { it.name }
.map { (_, list) ->
PersonFinal(list.first().name, list.first().age, list.sumBy { it.devices.count() })
}
Prints [PersonFinal(name=Jack, age=20, count=4), PersonFinal(name=John, age=20, count=0)]

Get updated id from entity autogenerate primary key

I created Alarm class and annotate it as Entity on Android Studio.
In this class I have put id variable as Entity Primary Key and it is auto generate.
#Entity(tableName = "alarm_data_table")
class Alarm(
#ColumnInfo(name = "alarm_hour")
var alarmHour: Int,
#ColumnInfo(name = "alarm_min")
var alarmMin: Int,
#ColumnInfo(name = "is_am")
var isAM: Boolean,
var days: ArrayList<Int>?,
#ColumnInfo(name = "is_on")
var isOn: Boolean,
var label: String,
#PrimaryKey(autoGenerate = true)
val id: Int = 0
) {
fun setAlarmSchOnOff(isOn: Boolean, activity: Activity){
if (isOn){setAlarmScheduleOn(activity)}
if (!isOn){setAlarmScheduleOff(activity)}
}
fun setAlarmScheduleOn (activity: Activity) {
val alarmManager = activity.getSystemService(Context.ALARM_SERVICE) as AlarmManager
Log.d("ALARM ON", Integer.toString(id))
val alarmIntent = Intent(activity, AlarmReceiver::class.java).let { intent ->
intent.putExtra(Constants().ALARM_LABEL_KEY,label)
PendingIntent.getBroadcast(
activity,
id,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
)
}
val calendar: Calendar = Calendar.getInstance().apply {
timeInMillis = System.currentTimeMillis()
set(Calendar.HOUR_OF_DAY, alarmHour)
set(Calendar.MINUTE, alarmMin)
}
days?.let {
it.forEach {
calendar.set(Calendar.DAY_OF_WEEK, it) }
alarmManager.setInexactRepeating(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
AlarmManager.INTERVAL_HOUR,
alarmIntent
)
} ?:run{
alarmManager.set(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
alarmIntent)
}
}
fun setAlarmScheduleOff(activity: Activity) {
val alarmManager = activity.getSystemService(Context.ALARM_SERVICE) as AlarmManager
Log.d("ALARM OFF", Integer.toString(alarmId))
val alarmIntent = Intent(activity, AlarmReceiver::class.java).let { intent ->
PendingIntent.getBroadcast(
activity,
id,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
)}
alarmManager.cancel(alarmIntent)
}
}
The problem is every time I tried to retrieve id for setAlarm method it will always return 0.
2019-10-30 15:38:07.441 11066-11066/com.andreasgift.myclock D/ALARMĀ ON: 0
Is there any way to return the id value after Entity update the value/ successfully insert it into table.
Is there any way to return the id value after Entity update the value/
successfully insert it into table
Change to use (optional)
:-
#PrimaryKey(autoGenerate = true)
var id: Long = 0 /*<<<<<<<<<< var not val + Long instead of Int */
you should really use Long for id columns as
a) the SQliteDatabase insert method that is ultimately used, returns a long and
b) (which is why a is as it is)) a rowid (which is effectively what autogenerate uses ) can be a value larger then an Int can hold (i.e. a 64 bit signed integer).
If you wanted to use Int for id then you could use myAlarm.id = (alarmDao.insertAlarm(myAlarm)).toInt() instead of myAlarm.id = alarmDao.insertAlarm(myAlarm)
And use a Dao
#Insert
fun insertAlarm(alarm :Alarm) :Long
i.e. set it so that it returns :Long
The insert now returns the id (or -1 if the insert didn't insert the row). If you still use the Alarm object after the insert then you could use something like :-
myAlarm.id = alarmDao.insertAlarm(myAlarm) /*<<<<<<<<<< SET THE id according to the inserted id */

How do I sum all the items of a list of integers in Kotlin?

I have a list of integers, like:
val myList = listOf(3,4,2)
Is there any quick way in Kotlin to sum all the values of the list? or do I have to use a loop?
Thanks.
You can use the .sum() function to sum all the elements in an array or collection of Byte, Short, Int, Long, Float or Double. (docs)
For example:
val myIntList = listOf(3, 4, 2)
myIntList.sum() // = 9
val myDoubleList = listOf(3.2, 4.1, 2.0)
myDoubleList.sum() // = 9.3
If the number you want to sum is inside an object, you can use sumOf to select the specific field you want to sum: (docs)
data class Product(val name: String, val price: Int)
val products = listOf(Product("1", 26), Product("2", 44))
val totalCost = products.sumOf { it.price } // = 70
Note: sumBy was deprecated in Kotlin 1.5 in favour of sumOf.
The above answer is correct, as an added answer, if you want to sum some property or perform some action you can use sumBy like this:
sum property:
data class test(val id: Int)
val myTestList = listOf(test(1), test(2),test(3))
val ids = myTestList.sumBy{ it.id } //ids will be 6
sum with an action
val myList = listOf(1,2,3,4,5,6,7,8,9,10)
val addedOne = myList.sumBy { it + 1 } //addedOne will be 65
The above answers are correct but, this can be another way if you also want to sum all integers, double, float inside a list of Objects
list.map { it.duration }.sum()
sumBy is Deprecated new use sumOf instead
val data = listOf(listOf(1, 2, 3), listOf(4, 5, 6))
println(data.sumOf { it.size }) // 6
println(data.sumOf { innerList -> innerList.sumOf { it } }) //21
println(data.sumOf { innerList -> innerList.sum() }) // 21

How to sum multiple elements after grouping in Kotlin

I have a list of objects A (alist).
A {
val b : Int
val c1 : Int
val c2 : Int
val d1 : Int
val d2 : Int
}
and I want to group them by b and calculate sum of c1+c2 and d1+d2 on each group and put the results in list of E objects elist.
E {
val sum_of_c_types : Int
val sum_of_d_types : Int
}
How do I achieve in kotlin using any collection inbuilt function?
note:
I know I can do it with reduce function and create temporary A objects, but this is important to dont use temporary A object in code.
I've solved it by using a sequence of groupBy, map and sumBy. It's probably not the cleanest solution I guess.
data class A(val b: Int,
val c1: Int,
val c2: Int,
val d1: Int,
val d2: Int)
data class E(val sumC: Int, val sumD: Int)
fun main(args: Array<String>) {
val alist = listOf(A(1, 2, 1, 4, 5), A(1, 3, 4, 6, 3), A(2, 2, 2, 2, 2), A(3, 1, 2, 1, 2))
val grouped: Map<Int, E> = alist.groupBy(A::b).mapValues {
E(it.value.sumBy { it.c1 + it.c2 }, it.value.sumBy { it.d1 + it.d2 })
}
grouped.forEach {
println("Group b=${it.key}: ${it.value}")
}
}
Results in:
Group b=1: E(sumC=10, sumD=18)
Group b=2: E(sumC=4, sumD=4)
Group b=3: E(sumC=3, sumD=3)
Edit:
With Grouping (using groupingBy instead of groupBy), it looks even better because you don't have to handle map entities:
val grouped = alist.groupingBy(A::b).aggregate { _, acc: E?, e, _ ->
E((acc?.sumC ?: 0) + e.c1 + e.c2, (acc?.sumD ?: 0) + e.d1 + e.d2)
}
I think it's just grouping with folding
fun group(a: List<A>) = a.groupingBy(A::b).fold(E(0, 0),
{ acc, elem ->
E(acc.sum_of_c_types + elem.c1 + elem.c2,
acc.sum_of_d_types + elem.d1 + elem.d2)
})
I solved it by following code:
alist.groupby { b }. mapValues {
it.value.map {
E(it.c1+it.c2, it.d1+it.d2)
}.reduce {
acc, e -> E(acc.sum_of_c_types + e.sum_of_c_types, acc.sum_of_d_types + e.sum_of_d_types)
}.values

Sum a subset of of numbers in a list

Is there a way in Kotlin for doing the sum() operation on a filtered list of numbers, without actually filtering out the elements first?
I'm looking for something like this:
val nums = listOf<Long>(-2, -1, 1, 2, 3, 4)
val sum = nums.sum(it > 0)
You can make use of Iterable<T>.sumBy:
/**
* Returns the sum of all values produced by [selector] function applied to each element in the collection.
*/
public inline fun <T> Iterable<T>.sumBy(selector: (T) -> Int): Int {
var sum: Int = 0
for (element in this) {
sum += selector(element)
}
return sum
}
You can pass a function to it where the function transforms negative value to 0. So, it sums up all values in the list which is greater than 0 since adding 0 makes no effect to the result.
val nums = listOf<Long>(-2, -1, 1, 2, 3, 4)
val sum = nums.sumBy { if (it > 0) it.toInt() else 0 }
println(sum) //10
If you require a Long value back, you have to write an extension for Long just like Iterable<T>.sumByDouble.
inline fun <T> Iterable<T>.sumByLong(selector: (T) -> Long): Long {
var sum: Long = 0
for (element in this) {
sum += selector(element)
}
return sum
}
Then, the toInt() conversion can be taken away.
nums.sumByLong { if (it > 0) it else 0 }
As suggested by #Ruckus T-Boom, if (it > 0) it else 0 can be simplified using Long.coerceAtLeast() which returns the value itself or the given minimum value:
nums.sumByLong { it.coerceAtLeast(0) }
sumBy and sumByDouble are Deprecated from kotlin 1.5 . You can check those link.
Use sumOf to get sum on a List or Array
sumOf
Returns the sum of all values produced by selector function applied to each element in the collection or Array.
Example:
data class Order(
val id : String,
val price : Double
)
val orderList = ......
val sum = orderList.sumOf { it.price }
data class Product(val name: String, val quantity: Int) {
}
fun main(args: Array<String>) {
val productList = listOf(
Product("A", 100),
Product("B", 200),
Product("C", 300)
)
val totalPriceInList1: Int = productList.map { it.quantity }.sum()
println("sum(): " + totalPriceInList1)
val totalPriceInList2: Int = productList.sumBy { it.quantity }
println("sumBy(): " + totalPriceInList2)
}
this is the result of our code
sum(): 600
sumBy(): 600