Scala how to group a map and then subgroup and transform values - scala

I have an object like this:
case class MyObject(x : Int,y : String,...) {
val buckets = 3
def bucket = x % buckets // returns a number between 0 and |buckets|
}
(x is an arbitrary number)
for example assume "buckets = 3" and we have many objects
MyObject(x = 0, y = "Something", ...)
MyObject(x = 1, y = "Something else", ...)
....
...
Using "groupBy" I collect "MYObjects" using the x % buckets, so it will be like:
val objects : Seq[MyObject] = ...
val groupedObjects : Map[Int: Seq[MyObjects]] = objects.groupBy(obj => x.bucket)
now I want to transform each value and also regroup to sublists of the different type
so lets say for each item in group = 1 , I want to nest under an additional layer and store a different calculated value:
so lets say if bucket 0 after the initial grouping looked like:
bucket[0] = [obj1,obj2,...,objn]
I want to be able to transform bucket "0" to contain another nested grouping:
bucket[0] = Map(sub_bucket_0 -> [transformed(objects)...], sub_bucket_1 -> [transformed(object)...),....]
meaning that eventually I have a data structure with the type:
Map[Int,Map[Sub_bucket_type,Seq[TransformedObject_type]]]

I think what you're looking for is mapValues() which will modify the Map's value elements to new values and/or types.
groupedObjects.mapValues(_.groupBy(/*returns new key type/value*/))
.mapValues(_.mapValues(_.map(/*transform MyObject elements*/)))

Related

Logic behind Two Number Sum Algorithm

Could someone explain to me the logic behind this hashMap algorithm? I'm getting confused about how the algorithm receives the total sum. I'm starting to learn about algorithms, so it's a little confusing for me. I made comments in my code to pinpoint each line code, but I'm not sure I'm grasping logic correctly. I'm just looking for an easier way to understand how the algorithm works to avoid confusing myself.
//**calculate Two Number Sum
func twoNumberSum(_ array: [Int], _ targetSum: Int) -> [Int] {
//1) initilize our Array to hold Integer Value: Boolean value to store value into hashTable
var numbersHashMap = [Int:Bool]()
//2) create placeHolder called number that iterates through our Array.
for number in array {
//3) variable = y - x
let match = targetSum - number
//4) ??
if let exists = numbersHashMap[match], exists {
//5) match = y / number = x
return [match, number] //
} else {
//6) Store number in HashTable and repeats
numbersHashMap[number] = true
}
}
return []
}
twoNumberSum([3,5,-4, 8, 11, 1, -1, -6], 10)
// x = Number
// y = Unknown *Solve for Y*
Sure, I can walk you through it. So we have a list of numbers, are we are trying to find two numbers that add together to make the specified target. To do this, for each number x, we check if (target - x) is in the list. If it is not, then we add x to the list. If it is, then we return x and (target - x).
Step 4 in your code is the part where we check if (target - x) is in the list. To see why this makes sense, let's walk through an example.
Say we have [2, 3, -1] and our target is 1. In this case, we first consider x = 2 and check our hashmap for (target - x) = (1 - 2) = -1. Since -1 is not in the hashmap, we add 2 to the hashmap. We then consider x = 3 and check for (1 - 3) = -2. Again, -2 is not in the hashmap, so we add it. Now we check x - -1. In this case, when we check (target - x) = (1 - (-1)) = 2, 2 is in the hashmap. Intuitively, we have already "seen" 2, and know that 2 and -1 can be added to get our value.
This is what provides the speed optimization over checking every two numbers in the list.

how do I get the index from this function

this is demo of iOS Charts library (LineChart) and I want to input my data instead of arc4random data.
My data is in Array so I have to approach with index but I can't understand the (0..<count).map { (i) -> ChartDataEntry code.
func setChartValues(_ count : Int = 24) {
let values = (0..<count).map { (i) -> ChartDataEntry in
let val = Double(arc4random_uniform(UInt32(count))+3)
return ChartDataEntry(x: Double(i), y: val)
}
let set1 = LineChartDataSet(entries: values , label : "DataSet 1")
let data = LineChartData(dataSet: set1)
self.lineChartView.data = data
}
It seems you are new to iOS and swift. What you are looking for is an understanding of the functionning of closures in swift, plus the map function which is called an high order function
from apple doc ( https://developer.apple.com/documentation/swift/array/3017522-map ) :
Returns an array containing the results of mapping the given closure over the sequence’s elements.
In other words it maps your array into another array, according to the trailing closure you passed as a parameter.
In your specific case here his how to read it :
(0..<count) : creates an array of count lengh
example : if count = 4 then (0..<count) is [0, 1, 2, 3]
As said previously the map function will transform each of your element into another ( therefore keeping the length of the array ).
in your case val = Double(arc4random_uniform(UInt32(count))+3) will be equal to a random number calculated with count value, and create a new ChartDataEntry with this random value.
to sum it up the whole code is just saying "I will create a count length array of random ChartDataEntry", I guess as a mockup
I suggest you to read about closures here :
https://medium.com/the-andela-way/closures-in-swift-8aef8abc9474
and high order functions ( such as map(_:) ) here :
https://medium.com/#abhimuralidharan/higher-order-functions-in-swift-filter-map-reduce-flatmap-1837646a63e8
let values = (0.. ChartDataEntry in
let val = Double(arc4random_uniform(UInt32(count))+3)
return ChartDataEntry(x: Double(i), y: val)
}
The value mapped and return is you can say a hash function. (arc4random).
It index you are taking is just setting X axis of the chart like 0 , 1 ,2 etc...
and your graph Y it set according to the functions return (arc4random)

Updating object value after applying Filter in Scala

I am having List of List's and calling it BAT in my code. Each BAT has 2 attribute. First one is Position and second is fitness. For Every List in BAT , i am computing its fitness using Sphere function. Based on fitness i have applied Filter which filter only those list whom fitness is less than a object called GF. This return me BAT.
My code is
var GlobalBest_Fitness = Double.PositiveInfinit
var BAT = List.fill(N)(new BAT1(d, MinVal, MaxVal))
BAT.map { x =>
x.fitness = sphere(x.position)
}
BAT.filter(_.Fitness < GF).map { x =>
GF = x.Fitness
}
def sphere(list: List[Double]): Double = {
list.foldLeft(0.0)((x, xs) => x + xs * xs)
}
class BAT1 ( dim:Int , min:Double , max:Double) {
val random = new Random()
var position : List[Double] = List.fill(dim)(random.nextDouble() * (max-min)+min )
var fitness :Double = math.random
}
This code set GF Fitness of last member of BAT but i want to set value of Object GF to Fitness of List with Lowest Fitness.
Here is some output to explain question. BAT with 5 Lists,
(List(-67.33460898977961, -71.09215709663737, 55.89607430834903, -43.23771807116002),14581.91575554507)
(List(90.12684307743376, 43.946793301728036, -93.06789837138616, -76.86083905559525),24623.390772205956)
(List(12.619843833260006, -86.17961848282789, 48.99208107528267, 24.69991428409682),10596.496873950442)
(List(96.24721330545535, 54.598176031247306, -92.20930457845513, -42.450241098519385),22549.06571516962)
(List(71.10095207554104, 74.02738064902607, 93.76767384566747, 40.917896190085656),21002.04935885428)
Output ==>> GF = 21002.04935885428
This is seting value of GF to last List fitness, it should instead set it to Lowest value that is 10596.496873950442 that is fitness of third List.
This List could be very large and have to iterate over it millions of time. I want to find optimal solution.
According to what I understood from the question, you want a minimum fitness value from a List of BAT1 objects ( List[BAT1] ).
Problems noticed:
No need to set fitness later by mapping, .map is used for
transformation of a list/collection/monad (e.g., you want to convert a
List[A] to List[B]).
So, your BAT1 class should look like:
class BAT1(dim:Int, min:Double, max:Double) {
val random = new Random()
var position: List[Double] = List.fill(dim)(random.nextDouble() * (max-min)+min )
val fitness: Double = sphere(position) //no need of var and can be directly computer here only
}
To get the lowest fitness, you don't need to filter
val minFitness = BAT.map(_.fitness).min
/*
we will first convert the list BAT which has type List[BAT1] to a
List[Double] containing fitness of each BAT1 object in the list
we will then get the minimum value in the List[Double] by .min method
List[BAT1] -> List[Double] -> Double
*/
Another way:
val minFitness = BAT.minBy(_.fitness).fitness //please read about minBy, maxBy in Scala documentations.

Scala: How to create a map over a collection from a set of keys?

Say I have a set of people Set[People]. Each person has an age. I want to create a function, which creates a Map[Int, Seq[People]] where for each age from, say, 0 to 100, there would be a sequence of people of that age or an empty sequence if there were no people of that age in the original collection.
I.e. I'm doing something along the lines
Set[People].groupBy(_.age)
where the output was
Map[Int, Seq[People]](0 -> Seq[John,Mary], 1-> Seq[People](), 2 -> Seq[People](Bill)...
groupBy of course omits all those ages for which there are no people. How should I implement this?
Configure a default value for your map:
val grouped = people.groupBy(_.age).withDefaultValue(Set())
if you need the values to be sequences you can map them
val grouped = people.groupBy(_.age).mapValues(_.toSeq).withDefaultValue(Seq())
Remember than, as the documentation puts it:
Note: `get`, `contains`, `iterator`, `keys`, etc are not affected by `withDefault`.
Since you've got map with not empty sequences corresponding to ages, you can fill the rest with empty collections:
val fullMap = (0 to 100).map (index => index -> map.getOrElse(index, None)).toMap

Scala: Iterate over mutable map of maps

I have defined a mutable map of maps
import scala.collection.mutable.Map
val default = Map.empty[String, Int].withDefaultValue(0)
val count = Map.empty[Any, Map[String, Int]].withDefaultValue(default)
which I populate/update as in
count("furniture")("table") += 1
count("furniture")("chair") = 6
count("appliance")("dishwasher") = 1
How can I iterate over all items in count? And why does count.keys return an empty Set()?
With default, does not create new Map when no value exists in collection, it just returns default value on such requests, and other changes are done on this default value.
count("furniture")("table") += 1
count("furniture")("chair") = 6
count("appliance")("dishwasher") = 1
count("banana") // will return Map with "table", "chair" & "dishwasher"
is equivalent
default("table") += 1
default("chair") = 6
default("dishwasher") = 1
And since you return this default value on any key, this default map will be returned on every call.
Your code will work like this.
count("furniture") = Map.empty[String, Int].withDefaultValue(0)
count("appliance") = Map.empty[String, Int].withDefaultValue(0)
count("furniture")("table") += 1
count("furniture")("chair") = 6
count("appliance")("dishwasher") = 1
There are several problems with your approach:
Issue #1:
val default = Map.empty[String,Int].withDefaultValue(0)
defines a value default. There is only one instance of this value and it can not be changed, since you defined as a val.
That means that your count map has a default value which is always the same instance of an empty map. Since count is empty, count("furniture") or count("appliance") is exactly the same as just default.
Issue #2:
withDefaultValue does not add entries to a map it just returns a default for undefined keys.
See #mavarazys answer