I have just started utilizing a Firestore server to store values from my android app. I have retrieved these values and now want to calculate an average from them. My original Idea was to get the values, convert them to a float (there are decimals so I am guessing this is the best format) and then calculate an average to display. However parts of this isn't working so I am wondering if there is a more optimal way.
Current code:
val map = document.data?.values //gets values from the hashmap
Log.d("firestore", "values1 = $map")
val map2 = map.toString() //converts them to string
Log.d("firestore", "values2 = $map2")
val map3 = map2.toFloat() //converts them to a float??
Log.d("firestore", "values3 = $map3")
The float section also returns random digits, for example the string is [5.0, 5.0]
and the float returns [91.0, 53.0, 46.0, 48.0, 44.0, 32.0, 53.0, 46.0, 48.0, 93.0]
. Any help on how I could convert the values so I can calculate the average of the values, would be much appreciated!
CodePudding user response:
Can you provide the input (document.data
) and the actual log output? It would help your understanding just as much anyone offering answer also to see the Kotlin inferred data types.
I assume the first variable map
is MutableCollection<Any!>
from your question. Calling it map
is misleading since map
is a key/value construct that requires two types to describe it.
When you call toString()
on a Collection
you are going to get a String representation. So I can believe the output is indeed [5.0, 5.0]
. It then makes no sense trying to convert that to float, as this whole string is not a float.
You are going about this the wrong way.
First you need to convert the Collection<Any!>
into a strongly typed array so you can do proper maths on it.
Let's start with some data we can see
val source : MutableMap<String, Any> = mutableMapOf("n1" to 2.0, "n2" to 3.5, "s1" to "fish", "n3" to 4.5)
val listOfAll: MutableCollection<Any> = source.values
println("listOfAll=$listOfAll")
This produces
listOfAll=[2.0, 3.5, fish, 4.5]
We can't perform calculations, so let's just filter out anything that is not a number
val listOfOnlyNumbers = listOfAll.filterIsInstance<Number>()
println("listOfOnlyNumbers=$listOfOnlyNumbers")
This produces
listOfOnlyNumbers=[2.0, 3.5, 4.5]
Now there is an in-built average function but Number
is abstract so we need cast to a concrete class. Float
would do, but most would normally use Double
(lower precision than Float
). If these values are Decimal
in origin BigDecimal
is better as it is optimised for decimals so you don't get 12.340000000000001
kind of thing.
val listOfDoubles = listOfOnlyNumbers.map { it.toDouble() }
val average = listOfDoubles.average()
println("average=$average")
Which produces what you may want
average=3.3333333333333335
(taking care that the original collection had one more value which is not taken into consideration for the average division)
So in summary what you want comes down to
val average = document.data?.values
.filterIsInstance<Number>()
.map { it.toDouble() }
.average()