3

I would appreciates some help from someone who knows what they are doing as opposed to me with Arrays in Swift for Xcode.

I have an array of over 40+ heart rate readinsg that I get from an Apple Watch and want to make a new array of only exactly 40 to show on a graph, but they have to be in order, I don't want them to be out of order, so I can't use random or shuffle and get the first 40 or the last 40 of the doubles.

e.g. heartratereadings = [56.0, 57.0, 58.0 ... 101.0]
var fortyReadings = ??heartratereading.somthing to get 40 values in the order they were in? 

What is a way I can do that? Thanks in advance ~ Kurt


Thanks you for the solution below I was able to great a cute graph on the watch that shows heart rate readings over time by using 40 instances of a line that is 200px hight. Then I use the individual heart rate to set the heights of each of the 40 bars. Looks great, obviously nt meant for any scientific or medical purpose, just gives me a rough idea of how the heart rate changes over given time. Apples simulator only gives heart rate readings from 58-62 so I can't wait to test. Thank you!!

Screenshot

4
  • 1
    Possible duplicate of How to return first 5 objects of Array in Swift? Commented Nov 15, 2019 at 9:03
  • Not exactly because I don’t want the first 40 or the last 40. I would like an evenly selected 40 from an array with more than 40 without mixing them up at all, like kept in the order they are recorded so I can make a graph. Commented Nov 15, 2019 at 10:11
  • 1
    You have no clear logic there. Come up with a clear logic. Commented Nov 15, 2019 at 11:31
  • @KurtLane: You should add that information to the question itself. Comments can be deleted at any time. Commented Nov 15, 2019 at 12:36

5 Answers 5

6

This is similar to what @staticVoidMan suggested. Instead of recursion, the indices of the new (smaller) array are mapped to indices of the old (larger) array via linear interpolation:

extension Array  {

    /// Return a smaller array by picking “evenly distributed” elements.
    ///
    /// - Parameter length: The desired array length
    /// - Returns: An array with `length` elements from `self`

    func pick(length: Int) -> [Element]  {
        precondition(length >= 0, "length must not be negative")
        if length >= count { return self }
        let oldMax = Double(count - 1)
        let newMax = Double(length - 1)
        return (0..<length).map { self[Int((Double($0) * oldMax / newMax).rounded())] }
    }
}

Examples:

let doubleArray = [56.0, 57.0, 58.0, 98.0, 101.0, 88.0, 76.0]
print(doubleArray.pick(length: 5))
// [56.0, 58.0, 98.0, 88.0, 76.0]

let intArray = Array(1...10)
print(intArray.pick(length: 8))
// [1, 2, 4, 5, 6, 7, 9, 10]
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for answering! Have a. great day
2

For starters, this basic extension on Array adds the functionality to return alternate elements from any type of array:

extension Array {
    
    func evenlySpaced(length: Int) -> [Element] {
        guard length < self.count else { return self }
        
        let takeIndex = (self.count / length) - 1
        let nextArray = Array(self.dropFirst(takeIndex + 1))
        return [self[takeIndex]] + nextArray.evenlySpaced(length: length - 1)
    }
    
}

Example 1:

let doubleArray = [56.0, 57.0, 58.0, 98.0, 101.0, 88.0, 76.0]
print(doubleArray.evenlySpaced(length: 5))

[56.0, 57.0, 58.0, 98.0, 101.0, 88.0, 76.0]
evenly spaced would give:
[56.0, 57.0, 58.0, 101.0, 76.0]


Example 2:

let intArray = (1...1000).map { $0 }
print(intArray.evenlySpaced(length: 40))

This shows that if you had an array of 1000 elements, the chosen interval values would be:

[25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300, 325, 350, 375, 400, 425, 450, 475, 500, 525, 550, 575, 600, 625, 650, 675, 700, 725, 750, 775, 800, 825, 850, 875, 900, 925, 950, 975, 1000]


It's a simple implementation and you could loosely say it's evenly spaced because it tends to favour the initial elements in data sets that are small when compared to the requested length.

Example 3:

let array = (1...10).map { $0 }
print(array.evenlySpaced(length: 8))

[1, 2, 3, 4, 5, 6, 8, 10]

You could instead implement a more balanced logic but the general idea would be the same.

3 Comments

This seams like exactly what I'm looking for. THANK YOU for answering.
Worked wonderfully on the simulator, have to test on a real watch now. Thanks again.
@KurtLane :) glad to help
0

Here is the simplest way to do so:

var heartRateReading = [56.0, 57.0, 58.0, 98.0, ..., 101.0]

//If you want 40 items from the 10th position
var fortySelected = heartRateReading.suffix(from: 10).suffix(40)

// If you want to iterate each second and get 40 values out of it
_ = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { (_) in
  let values = heartRateReading.suffix(40)
  print(values)
}

3 Comments

Aren't heart rate readings sorted by the time they sampled? Like sorting your heart pulsing frequencies by the value seems like meaningless data.
OP clearly says he needs them in order. Not sort them in any way. Sorting them in ascending or descending order would never be a usecase for anyone for this type of data.
Hi, sorry if I wasn’t clear. I don’t want to mix them up at all. Acending or decending is no good and the first forty and last forty are not what I’m looking for. I do need them in order that they are recorded without mixing them up. How to get only 40 sequential readings from an array with more than 40. Like if there are 80 get every second number but also if there are 50 get forty from that witjout mixing them up.
0

To get X number of elements from Y index we can have generic function like below:


func fetchElements<T>(sourceArray: [T],
                      startIndex: Int,
                      recordCount: Int) -> [T] {
    
    guard startIndex >= 0 && recordCount >= 0 else { return [] }
    
    guard startIndex < sourceArray.count else { return [] }
    
    let arrTrimmedFromStartIndex = Array(sourceArray.suffix(from: startIndex))
    let arrWithRequiredCount = Array(arrTrimmedFromStartIndex.prefix(recordCount))
    
    return arrWithRequiredCount
}

Now we can use it with any type as below:

class MyModel {
    
    let name: String
    
    init(name: String) {
        self.name = name
    }
}

Call to actual function:

let arr = (0..<10).map({ MyModel(name: "\($0)") })

let result = fetchElements(sourceArray: arr,
                           startIndex: 4,
                           recordCount: 3)

print(arr.map({$0.name}))
print(result.map({$0.name}))

Print result:

["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
["4", "5", "6"]

Hope this helps!

Comments

-1

Just use a for loop

Var forty = [double]()
For n in 1...40 {
     forty.insert(heartRate[n], at: 0)
}

1 Comment

Thank you for answering! Have a. great day

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.