Averaging elements in array of arrays by index using functional programming - arrays

I have an array of arrays of Doubles. For example:
let mceGain = [[3,4,5],[7,4,3],[12,10,7]] // Written as integers for simplicity here
I would now like to average the elements in the different arrays with corresponding indexes. So I would have an output looking somewhat like this:
//firstAvg: (3+7+12)/3 = 7.33
//secondAvg: (4+4+10)/3 = 6
//thirdAvg: (5+3+7)/3 = 5
Then finally I would like to store these averages in a simpler array:
//mceGain: [7.33,6,5]
I have tried to do this with a double for-loop with a switch-statement inside, but this seems to be unnecessarily complicated. I assume the same result could be achieved using a combination of reduce(), map() and filter(), but I cannot seem to wrap my head around it.

Let's analyse what you want to do here. You start with an array of arrays:
[[3,4,5],[7,4,3],[12,10,7]]
and you want to transform each subarray into a number:
[7,6,5]
Whenever you have this kind of "transform each element of this sequence into something else" situation, use map.
When you compute the average, you need to transform a sequence of things into just one thing. This means that we need reduce.
let array: [[Double]] = [[3,4,5],[7,4,3],[12,10,7]]
let result = array.map { $0.reduce(0.0, { $0 + $1 }) / Double($0.count) }
With comments:
let array: [[Double]] = [[3,4,5],[7,4,3],[12,10,7]]
let result = array.map { // transform each element like this:
$0.reduce(0.0, { $0 + $1 }) // sums everything in the sub array up
/ Double($0.count) } // divide by count
EDIT:
What you need to do is to "transpose" the array first, then do the map and reduce:
array[0].indices.map{ index in // these three lines makes the array [[3, 7, 12], [4, 4, 10], [5, 3, 7]]
array.map{ $0[index] }
}
.map { $0.reduce(0.0, { $0 + $1 }) / Double($0.count) }

This should answer your comment below
let elms: [[Double]] = [[3, 5, 3], [4, 4, 10] , [5, 3, 7]]
func averageByIndex(elms:[[Double]]) -> [Double]? {
guard let length = elms.first?.count else { return []}
// check all the elements have the same length, otherwise returns nil
guard !elms.contains(where:{ $0.count != length }) else { return nil }
return (0..<length).map { index in
let sum = elms.map { $0[index] }.reduce(0, +)
return sum / Double(elms.count)
}
}
if let averages = averageByIndex(elms: elms) {
print(averages) // [4.0, 4.0, 6.666666666666667]
}

Related

How to split a sequential integer array, if any sequence is missing

Supposre I have an input array of integers. I want to split this array in multiple array based on the missing integer and append it in a new Array. I think split can be used here but not sure how to do it. I want arrayFinal only.
myArray = [0,1,2,4,7,8]
Desired Output
arrayOne = [0,1,2]
arrayTwo = [4]
arrayThree = [7,8]
arrayFinal = [[0,1,2], [4], [7,8]]
That's an algorithm you're asking for so there are a dozen different ways to do it. Since you are going to have to walk through the array's contents to find the missing integers, I would just create an array and append the numbers to it as you go, then create a new array whenever you hit a gap.
You'll probably have to adjust this for any special cases you might have. "Will this always start at 0 and move in a positive direction?" etc.
Try this out:
func splitByMissingInteger(array: [Int]) -> [[Int]]? {
var arrayFinal :[[Int]] = [ [Int]() ]
var i = 0
for num in array{
if arrayFinal[i].isEmpty || (arrayFinal[i].last == nil){
arrayFinal[i].append(num)
} else if num == (arrayFinal[i].last! + 1){
arrayFinal[i].append(num)
} else {
i += 1
arrayFinal.append([Int]())
arrayFinal[i].append(num)
}
}
return arrayFinal
}
You can just sort your array and iterate them in order. Check if the element minus one is equal to the last element of your 2D array, if true append otherwise append a new array with the element and increase the index of the subarrays:
extension Collection where Element == Int {
func grouped() -> [[Element]] {
let elements = Set(self).sorted()
guard let first = elements.first else { return [] }
var result = [[first]]
var i = 0
for element in elements.dropFirst() {
if element-1 == result[i].last! {
result[i].append(element)
} else {
result.append([element])
i += 1
}
}
return result
}
}
let myArray = [0,1,2,4,7,8]
let grouped = myArray.grouped() // [[0, 1, 2], [4], [7, 8]]

Get a set number of random items from array [duplicate]

This question already has answers here:
Get random elements from array in Swift
(6 answers)
Closed 5 years ago.
I want to write an extension which allows me to put in an array and return a set number of elements from that array with no repeated items. How would I do that? This is what I have so far but it is not perfect. It does not consider duplicates and it does not seem like the best way for this to be done. I was thinking it might make sense to use a set for duplicates.
extension Array {
func randomElement(numberOfItems:Int) -> [Element] {
var finalReturn = Array()
for i in 0..<numberOfItems {
finalReturn.append(self[Int(arc4random_uniform(UInt32(self.count)))])
}
return finalReturn
}
}
usage should be like this.
let selected = allData.randomElement(numberOfItems: 10)
Here is one way to do it:
extension Array {
func randomElements(number: Int) -> [Element] {
guard number > 0 else { return [Element]() }
var remaining = self
var chosen = [Element]()
for _ in 0 ..< number {
guard !remaining.isEmpty else { break }
let randomIndex = Int(arc4random_uniform(UInt32(remaining.count)))
chosen.append(remaining[randomIndex])
remaining.remove(at: randomIndex)
}
return chosen
}
}
Sample:
let testArray = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let randomThree = testArray.randomElements(number: 3)
// randomThree is [1, 5, 4]
Depending on your use case, you may want to change the behavior when the number of elements requested is greater than the number of elements in the array.
In my sample above, if this is the case, I return the maximum number of elements possible (the number of elements in the original array). Alternatively, you could give an error or return nil.

Transforming RandomAccessSlice to RandomAccessCollection

When we try to retrieve a range of elements from an Array, we get back an ArraySlice:
let array = [1, 3, 5, 2]
let arraySlice = array[..<2] // elements up to index 1 == [1, 3]
We can transform it back to the Array type like so:
let arrayFromSlice = Array(arraySlice)
Let's say you want to create a method that returns the first 3 elements of any RandomAccessCollection:
func first3Elements<T: RandomAccessCollection>(_ c: T) -> T {
let slice = c.prefix(3)
// COMPILER ERROR: non-nominal type 'T'
// does not support explicit initialization
return T(slice)
}
Is it possible to perform this conversion?
Here my first attempt using type erasure but I guess there are better solutions.
func first3Elements<T>(_ c: AnyRandomAccessCollection<T>) -> AnyRandomAccessCollection<T> {
let slice = c.prefix(3)
return AnyRandomAccessCollection(slice)
}
let array = AnyRandomAccessCollection([1, 2, 3, 4])
let result = first3Elements(array)
for x in result {
print(x)
}

swift array storing data

I made a function that returns multiple values
let interestingNumbers = [ // String:Array<Int>
"Prime": [2, 3, 5, 7, 11, 23],
"Fibonacci": [1, 1, 2, 3, 5, 80],
"Square": [1, 4, 9, 16, 25],
]
func largestNum(objDictionary:[String:Array<Int>]) -> (Int,String) {
var largest = 0
var ki:String? = nil
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
ki=kind
}
}
}
return (largest , ki!)
}
print(largestNum(interestingNumbers)) //calling fuction and print
/*var ar2:[Int,String] = largestNum(interestingNumbers))
print(ar2)*/' this code have an error`
How can I store the returned values from the function in the array
Update:
If you want both values in a single array with ar[0] being the Int and ar[1] being the String, then you'll need to declare ar2 to be [Any] and unpack the tuple when initializing ar2:
let largest = largestNum(interestingNumbers)
var ar2:[Any] = [largest.0, largest.1]
print(ar2) // [80, "Fibonacci"]
If you just assign the return to ar2 and leave it as a tuple, you can access the values with ar2.0 and ar2.1:
var ar2 = largestNum(interestingNumbers)
print(ar2.0) // 80
print(ar2.1) // "Fibonacci"
Or if you change your largestNum to return a named tuple:
func largestNum(objDictionary:[String:Array<Int>]) -> (number: Int, kind: String) {
}
var ar2 = largestNum(interestingNumbers)
print(ar2.number) // 80
print(ar2.kind) // "Fibonacci"
Original Answer:
Declare your array ar2 to hold tuples pairs of Int and String, and then wrap your return value in [] to create an array:
var ar2:[(Int,String)] = [largestNum(interestingNumbers)]
print(ar2) // [(80, "Fibonacci")]
Because tuples are really meant for temporary values, it is better style to store values in an array using a struct:
Change your function to return an InterestingNumber:
struct InterestingNumber {
let kind: String
let number: Int
}
func largestNum(objDictionary:[String:Array<Int>]) -> InterestingNumber {
// contents omitted for brevity
return InterestingNumber(kind: ki!, number: largest)
}
let largest = largestNum(interestingNumbers)
// Define your array to hold `InterestingNumber`s:
var ar2:[InterestingNumber] = [largest]
print(ar2) // [InterestingNumber(kind: "Fibonacci", number: 80)]
If you meant for ar2 to just hold a single value, then simply do:
var ar2 = largestNum(interestingNumbers)
and Swift will infer the type (which is a tuple in your original code or an InterestingNumber when using the struct.
your code runs fine in xcode 7.3.1 playground
okay, now i get your question:
let z: (Int, String) = largestNum(interestingNumbers)
The part after the arrow in your function definition is the type (i think called tupel), you can use it for a variable.

How to Remove Every Other Element in an Array in Swift?

So say I have an array:
var stringArray = ["a","b","c","d","e","f","g","h","i","j"]
Now, how do I delete "a", "c", "e", "g", and "i" (all the even number indexes from the array)?
Thanks!
Instead of using C-style for-loops (which are set to be deprecated in an upcoming version of Swift), you could accomplish this using strides:
var result = [String]()
for i in stride(from: 1, through: stringArray.count - 1, by: 2) {
result.append(stringArray[i])
}
Or for an even more functional solution,
let result = stride(from: 1, to: stringArray.count - 1, by: 2).map { stringArray[$0] }
Traditional
var filteredArray = []
for var i = 1; i < stringArray.count; i = i + 2 {
filteredArray.append(stringArray[i])
}
Functional alternative
var result = stringArray.enumerate().filter({ index, _ in
index % 2 != 0
}).map { $0.1 }
enumerate takes a array of elements and returns an array of tuples where each tuple is an index-array pair (e.g. (.0 3, .1 "d")). We then remove the elements that are odd using the modulus operator. Finally, we convert the tuple array back to a normal array using map. HTH
There are a bunch of different ways to accomplish this, but here are a couple that I found interesting:
Using flatMap() on indices:
let result: [String] = stringArray.indices.flatMap {
if $0 % 2 != 0 { return stringArray[$0] }
else { return nil }
}
Note: result needs to be defined as a [String] otherwise the compiler doesn't know which version of flatMap() to use.
Or, if you want to modify the original array in place:
stringArray.indices.reverse().forEach {
if $0 % 2 == 0 { stringArray.removeAtIndex($0) }
}
In this case you have to call reverse() on indices first so that they're enumerated in reverse order. Otherwise by the time you get to the end of the array you'll be attempting to remove an index that doesn't exist anymore.
Swift 4.2
A function accepting generics and producing reduced result
func stripElements<T>(in array:[T]) -> [T] {
return array.enumerated().filter { (arg0) -> Bool in
let (offset, _) = arg0
return offset % 2 != 0
}.map { $0.element }
}

Resources