How to average similar tuples in an array of tuples in Swift - arrays

I really need your help. I have an array of tuples that looks like this:
[("07-21-2016", 5), ("07-21-2016", 1), ("07-21-2016", 2), ("07-21-2016", 3), ("07-21-2016", 4), ("07-21-2016", 5), ("07-20-2016", 6), ("07-20-2016", 5), ("07-19-2016", 5)]
I need to take all the tuples with the same date and average them out. So at the end it would look like:
[("07-21-2016", 33.3), ("07-20-2016", 5.5), ("07-19-2016", 5)]
Does anyone know how to do this?

let array = [("07-21-2016", 5), ("07-21-2016", 1), ("07-21-2016", 2), ("07-21-2016", 3), ("07-21-2016", 4), ("07-21-2016", 5), ("07-20-2016", 6), ("07-20-2016", 5), ("07-19-2016", 5)]
// Create dictionary to hold mapping of date to array of values
var dict = [String: [Double]]()
// use forEach to add each value to the array for each key
array.forEach {(date, num) in dict[date] = (dict[date] ?? []) + [Double(num)]}
// use map with reduce to find the average of each value and return a tuple
// containing the date and the average value
let result = dict.map {(date, nums) in (date, nums.reduce(0, combine: +) / Double(nums.count))}
print(result)
Output:
[("07-20-2016", 5.5), ("07-19-2016", 5.0), ("07-21-2016", 3.3333333333333335)]
Explanation:
array.forEach {(date, num) in dict[date] = (dict[date] ?? []) + [Double(num)]}
forEach takes each tuple of the array, looks up the array of values corresponding to dict[date] and appends the new num to that array. If dict[date] returns nil, then this is the first time we've seen this key, so use the nil coalescing operator ?? to return an empty array [] and append the new value to that.
At the end of this, the contents of dict is:
["07-20-2016": [6.0, 5.0], "07-19-2016": [5.0], "07-21-2016": [5.0, 1.0, 2.0, 3.0, 4.0, 5.0]]
let result = dict.map {(date, nums) in (date, nums.reduce(0, combine: +) / Double(nums.count))}
When map is applied to a dictionary, it takes each (key, value) pair and creates a new value based upon that. The end result of map is a new array of the values it returns. In this case, the value returned for each iteration of map is a tuple containing the date and the average of the numbers associated with that date.
nums.reduce(0, combine: +)
This sums the values in the nums array. reduce takes an initial value (0 in this case) and a closure that will be evaluated for each value in the nums array. Each iteration of reduce takes the current running total and the next value in nums and sums them. This sum is then divided by Double(nums.count) to produce the average. Finally, map returns (date, avg) which produces the final result.

Here's one way (using a dictionary to collate the numbers):
let dateValues = [("07-21-2016", 5), ("07-21-2016", 1), ("07-21-2016", 2), ("07-21-2016", 3), ("07-21-2016", 4), ("07-21-2016", 5), ("07-20-2016", 6), ("07-20-2016", 5), ("07-19-2016", 5)]
var averages:[String:(Int,Int)] = [:]
for (date,value) in dateValues
{
averages[date] = averages[date] ?? (0,0)
averages[date] = (averages[date]!.0 + value, averages[date]!.1 + 1)
}
let averagePerDate = averages.map{($0,Float($1.0)/Float($1.1))}.sort{$0.0>$1.0}
print(averagePerDate)
// [("07-21-2016", 3.33333325), ("07-20-2016", 5.5), ("07-19-2016", 5.0)]
And a more concise one using sets:
let dateList = dateValues.reduce( Set<String>(), combine: { $0.union(Set([$1.0])) })
let dateData = dateList.map{ date in return (date, dateValues.filter({$0.0==date}).map{$0.1}) }
let dateCounts = dateData.map{ ($0, $1.reduce(0,combine:+), Float($1.count) ) }
let dateAverages = dateCounts.map{ ($0, Float($1/$2) ) }.sort{$0.0>$1.0}
print(dateAverages)

Related

Finding and appending an index of an array to the actual element inside the array

I am having an array of objects. Each object has a property called a number.
struct Age {
var position = 0
}
I also have an array of those objects:
let age = Age()
let array = [age]
How can I append to the position property the index of the element in the array.
Something like:
for i in array {
i.position = i.index
}
Basically, the first item of the array which is at position 0 should make the i=0 second to 1,etc.
array must be var and not let in order to be mutable and make this work:
var array = Array(repeating: Age(), count: 10) //build a sample array to test with
for index in 0..<array.count {
array[index].position = index
}
print(array)
Which yields the following:
[StackOverflowPlayground.Age(position: 0),
StackOverflowPlayground.Age(position: 1),
StackOverflowPlayground.Age(position: 2),
StackOverflowPlayground.Age(position: 3),
StackOverflowPlayground.Age(position: 4),
StackOverflowPlayground.Age(position: 5),
StackOverflowPlayground.Age(position: 6),
StackOverflowPlayground.Age(position: 7),
StackOverflowPlayground.Age(position: 8),
StackOverflowPlayground.Age(position: 9)]

Kotlin - index of duplicate elements in an array

Is there an easy way to get the index of matching elements in an array without writing a for loop and then collecting the index?
Eg: val arr = arrayOf<Int>(2,3,4,2,5,2,6,3,2)
Output: For element 2, output should be (0,3,5,8) and For element 3, output should be (1,7)
Kotlin provides indexOf(element) which returns first index and then lastIndexOf(element) which will give me last index. Am looking for an easy way to get indexes of all matching elements.
My solution: Standard solution of iterating over the array and collecting index.
var indexArrOf2 = mutableListOf<Int>()
var indexArrOf3 = mutableListOf<Int>()
for(i in arr.indices) {
if (arr[i] == 2) {
indexArrOf2.add(i)
}
if (arr[i] == 3) {
indexArrOf3.add(i)
}
}
Yes, you can use mapIndexedNotNull:
arr.mapIndexedNotNull { index, elem -> if (elem == 2) index else null }
A little convoluted but returns a map Map<Element, List<Indices>> to use as desired.
arr.mapIndexed { index, i -> i to index } //List<Pair<Int, Int>> - [(2, 0), (3, 1), (4, 2), (2, 3), (5, 4), (2, 5), (6, 6), (3, 7), (2, 8)]
.groupBy { it.first } //Map<Int, List<Pair<Int, Int>>> - {2=[(2, 0), (2, 3), (2, 5), (2, 8)], 3=[(3, 1), (3, 7)], 4=[(4, 2)], 5=[(5, 4)], 6=[(6, 6)]}
.mapValues { it.value.map { it.second } } //Map<Int, List<Int>> - {2=[0, 3, 5, 8], 3=[1, 7], 4=[2], 5=[4], 6=[6]}
I'd use the withIndex() extension function to add the index.  You can then select the matching items, and get their indices:
val arr = arrayOf(2, 3, 4, 2, 5, 2, 6, 3, 2)
println(arr.withIndex().filter{ it.value == 2 }.map{ it.index })
// prints '[0, 3, 5, 8]'

Read values and list of lists in Haskell

Before to mark this question as duplicated, I already read this topic: Haskell read Integer and list of lists from file and the solution doesn't solve my problem.
I'm trying to read the content in a File that contains this structure:
String, String, [(Int, Int, Int)]
The file looks something like this:
Name1 22/05/2018 [(1, 5, 10), (2, 5, 5), (3, 10, 40)]
Name2 23/05/2018 [(1, 10, 10), (2, 15, 5), (3, 50, 40),(4,20,5)]
Name3 22/05/2018 [(4, 2, 1), (5, 2, 2), (6, 50, 3), (1,2,3)]
Name4 23/05/2018 [(1, 3, 10), (2, 1, 5), (3, 2, 40), (6,20,20)]
In Haskell, I created this function to read the contents of the file and "convert" this content to my custom type.
rlist :: String -> [(Int, Int, Int)]
rlist = read
loadPurchases :: IO [(String, String, [(Int, Int, Int)])]
loadPurchases = do s <- readFile "tes.txt"
return (glpurch (map words (lines s)))
glpurch :: [[String]] -> [(String, String, [(Int, Int, Int)])]
glpurch [] = []
gplpurch ([name, dt, c]:r) = (name, dt, (rlist c)) : gplpurch r
But when I try to execute the "loadPurchases" function, I get this error:
Non-exhaustive patterns in function glpurch.
Using :set -Wall, I received this help message:
<interactive>:6:1: warning: [-Wincomplete-patterns]
Pattern match(es) are non-exhaustive
In an equation for `glpurch':
Patterns not matched:
([]:_:_)
([_]:_)
([_, _]:_)
((_:_:_:_:_):_)
My problem is how to create all these conditions.
I will be very grateful if anyone can help me create those conditions that are likely to determine the "stopping condition"
You are only matching lists of length 3 when in fact there are many more words on each line. Just try it in GHCi:
> words "Name1 22/05/2018 [(1, 5, 10), (2, 5, 5), (3, 10, 40)]"
["Name1","22/05/2018","[(1,","5,","10),","(2,","5,","5),","(3,","10,","40)]"]
You probably want to recombine all words past the first two:
glpurch ((name : dt : rest) :r) = (name, dt, (rlist $ unwords rest)) : gplpurch r
To solve my problem, I did what #Welperooni and #Thomas M. DuBuisson suggested.
I added this code to my function:
glpurch ((name: dt: c: _): r) = (name, dt, (read c :: [(Cod, Quant, Price)
And I removed the blanks that were in the list in my file, these spaces made the division of the text not done correctly.

How to sum up every column of a Scala array?

If I have an array of array (similar to a matrix) in Scala, what's the efficient way to sum up each column of the matrix? For example, if my array of array is like below:
val arr = Array(Array(1, 100, ...), Array(2, 200, ...), Array(3, 300, ...))
and I want to sum up each column (e.g., sum up the first element of all sub-arrays, sum up the second element of all sub-arrays, etc.) and get a new array like below:
newArr = Array(6, 600, ...)
How can I do this efficiently in Spark Scala?
There is a suitable .transpose method on List that can help here, although I can't say what its efficiency is like:
arr.toList.transpose.map(_.sum)
(then call .toArray if you specifically need the result as an array).
Using breeze Vector:
scala> val arr = Array(Array(1, 100), Array(2, 200), Array(3, 300))
arr: Array[Array[Int]] = Array(Array(1, 100), Array(2, 200), Array(3, 300))
scala> arr.map(breeze.linalg.Vector(_)).reduce(_ + _)
res0: breeze.linalg.Vector[Int] = DenseVector(6, 600)
If your input is sparse you may consider using breeze.linalg.SparseVector.
In practice a linear algebra vector library as mentioned by #zero323 will often be the better choice.
If you can't use a vector library, I suggest writing a function col2sum that can sum two columns -- even if they are not the same length -- and then use Array.reduce to extend this operation to N columns. Using reduce is valid because we know that sums are not dependent on order of operations (i.e. 1+2+3 == 3+2+1 == 3+1+2 == 6) :
def col2sum(x:Array[Int],y:Array[Int]):Array[Int] = {
x.zipAll(y,0,0).map(pair=>pair._1+pair._2)
}
def colsum(a:Array[Array[Int]]):Array[Int] = {
a.reduce(col2sum)
}
val z = Array(Array(1, 2, 3, 4, 5), Array(2, 4, 6, 8, 10), Array(1, 9));
colsum(z)
--> Array[Int] = Array(4, 15, 9, 12, 15)
scala> val arr = Array(Array(1, 100), Array(2, 200), Array(3, 300 ))
arr: Array[Array[Int]] = Array(Array(1, 100), Array(2, 200), Array(3, 300))
scala> arr.flatten.zipWithIndex.groupBy(c => (c._2 + 1) % 2)
.map(a => a._1 -> a._2.foldLeft(0)((sum, i) => sum + i._1))
res40: scala.collection.immutable.Map[Int,Int] = Map(2 -> 600, 1 -> 6, 0 -> 15)
flatten array and zipWithIndex to get index and groupBy to map new array as column array, foldLeft to sum the column array.

Removing arrays which are subsets of other arrays in Scala

I have an array of array of integers. Like:
val t1 = Array(Array(1, 2, 3), Array(2), Array(4, 5, 6), Array(5, 6))
I want to remove the arrays that are subsets of another array. So, the result should be:
Array(Array(1, 2, 3), Array(4, 5, 6))
Ideally, these should be Sets, but in the context of my program, they are arrays, and I don't want to convert them to sets due to performance reasons.
I solved it this way in Scala, but I would like to know if there is a more elegant (and/or more efficient) way to do this:
def removeSubsets[T: ClassManifest](clusters: Array[Array[T]]) = {
val sortedClusters = clusters.sortBy(-1 * _.length)
sortedClusters.foldLeft(Array[Array[T]]()){ (acc, ele) =>
val isASubset = acc.exists(arr => (ele diff arr).length == 0)
if (isASubset) acc else acc :+ ele
}
}

Resources