How to use range.map in swift 3/4? - arrays

I have the code below, which works in swift 2.3. I am struggling to understand how to convert it to swift 3/4 - the issue it those is
Value of type 'Range<Int>' has no member 'map'
let grainSize = CGFloat(0.01)
let min = CGFloat(-3)
let max = CGFloat(3)
let range = Range<Int>(uncheckedBounds: (lower: Int(min/grainSize), upper: Int(max/grainSize)))
let lol = NSRange(range)
var points = range.map { step -> CGPoint in
let i = grainSize * CGFloat(step)
let x = x_base + (i * controller.width / 4.0)
let y = y_base + (m * equation(i))
if x_init == CGFloat.max { x_init = x }
return CGPointMake(x, y)
}
points.append(CGPointMake(x_init, y_base))
points.forEach { renderer.lineTo($0) }
I am wondering if someone can point me in the right direction for this - even to documentation regarding this, as I can't find anything about it in apple docs either =[

Range does not adopt Sequence, just create the range literally as CountableClosedRange
let range = Int(min/grainSize)...Int(max/grainSize)

Related

How to check if value is between two values stored in an array

I have searched around trying to find a similar problem, however I do not know the correct wording. I have an array of CGFloats and then a separate saved CGFloat. I want to check if the single CGFloat is between the values stored in the array.
I currently have this in a loop starting at the end of the array and checking if it is smaller than the value. It then moves from right to left until it is not smaller and returns the index. Is there a built in method or a cleaner way to do this?
For example:
var pressureLevels:[CGFloat] = [0, 1.0, 2.133333333333333, 3.266666666666667, 4.4, 5.533333333333334, 6.666666666666667]
var tempPressure: CGFloat = 4.877777
return 4 or return 5
I want to be able to tell that tempPressure is in pressureLevels between 4.4 and 5.533333333333334 and return the index (either 4 or 5).
This will return the higher index, for the next larger value. I have assumed from your example that the array is already sorted
let largerIndex = pressureLevels.firstIndex(where: { $0 > tempPressure})
let smallerIndex = largerIndex - 1
Note the edge cases, if there is no value larger than tempPressure then largerIndex will be nil and if all values in the array are larger then largerIndex will be 0
Joakim Danielson's way will work fine for sorted arrays. If your array is unsorted you could put the logic in an extension of the Array class like so:
extension Array where Element == CGFloat {
func huggingIndexes(for value:CGFloat) -> (lower:Int?, upper:Int?) {
var largestLowerBound:CGFloat?
var lowerIndex:Int?
var smallestUpperBound:CGFloat?
var upperIndex:Int?
for (index, val) in self.enumerated() {
if val < value, val >= largestLowerBound ?? val {
largestLowerBound = val
lowerIndex = index
} else if val > value, val <= smallestUpperBound ?? val {
smallestUpperBound = val
upperIndex = index
}
}
return (lowerIndex, upperIndex)
}
}
var pressureLevels:[CGFloat] = [2.133333333333333, 4.4, 6.666666666666667, 1.0, 5.533333333333334, 0.0, 3.266666666666667]
var tempPressure: CGFloat = 4.877777
let huggingIndexes = pressureLevels.huggingIndexes(for: tempPressure)
print(huggingIndexes.lower) //Prints 1
print(huggingIndexes.upper) //Prints 4

Dereference UnsafeMutablePointer<UnsafeMutableRawPointer>

I have a block that is passing data in that I'd like to convert to an array of array of floats -- e.g. [[0.1,0.2,0.3, 1.0], [0.3, 0.4, 0.5, 1.0], [0.5, 0.6, 0.7, 1.0]]. This data is passed to me in the form of data:UnsafeMutablePointer<UnsafeMutableRawPointer> (The inner arrays are RGBA values)
fwiw -- the block parameters are from SCNParticleEventBlock
How can I dereference data into a [[Float]]? Once I have the array containing the inner arrays, I can reference the inner array (colorArray) data with:
let rgba: UnsafeMutablePointer<Float> = UnsafeMutablePointer(mutating: colorArray)
let count = 4
for i in 0..<count {
print((rgba+i).pointee)
}
fwiw -- this is Apple's example Objective-C code for referencing the data (from SCNParticleSystem handle(_:forProperties:handler:) )
[system handleEvent:SCNParticleEventBirth
forProperties:#[SCNParticlePropertyColor]
withBlock:^(void **data, size_t *dataStride, uint32_t *indices , NSInteger count) {
for (NSInteger i = 0; i < count; ++i) {
float *color = (float *)((char *)data[0] + dataStride[0] * i);
if (rand() & 0x1) { // Switch the green and red color components.
color[0] = color[1];
color[1] = 0;
}
}
}];
You can actually subscript the typed UnsafeMutablePointer without having to create an UnsafeMutableBufferPointer, as in:
let colorsPointer:UnsafeMutableRawPointer = data[0] + dataStride[0] * i
let rgbaBuffer = colorsPointer.bindMemory(to: Float.self, capacity: dataStride[0])
if(arc4random_uniform(2) == 1) {
rgbaBuffer[0] = rgbaBuffer[1]
rgbaBuffer[1] = 0
}
Were you ever able to get your solution to work? It appears only a handful of SCNParticleProperties can be used within an SCNParticleEventBlock block.
Based on this answer, I've written the particle system handler function in swift as:
ps.handle(SCNParticleEvent.birth, forProperties [SCNParticleSystem.ParticleProperty.color]) {
(data:UnsafeMutablePointer<UnsafeMutableRawPointer>, dataStride:UnsafeMutablePointer<Int>, indicies:UnsafeMutablePointer<UInt32>?, count:Int) in
for i in 0..<count {
// get an UnsafeMutableRawPointer to the i-th rgba element in the data
let colorsPointer:UnsafeMutableRawPointer = data[0] + dataStride[0] * i
// convert the UnsafeMutableRawPointer to a typed pointer by binding it to a type:
let floatPtr = colorsPointer.bindMemory(to: Float.self, capacity: dataStride[0])
// convert that to a an UnsafeMutableBufferPointer
var rgbaBuffer = UnsafeMutableBufferPointer(start: floatPtr, count: dataStride[0])
// At this point, I could convert the buffer to an Array, but doing so copies the data into the array and any changes made in the array are not reflected in the original data. UnsafeMutableBufferPointer are subscriptable, nice.
//var rgbaArray = Array(rgbaBuffer)
// about half the time, mess with the red and green components
if(arc4random_uniform(2) == 1) {
rgbaBuffer[0] = rgbaBuffer[1]
rgbaBuffer[1] = 0
}
}
}
I'm really not certain if this is the most direct way to go about this and seems rather cumbersome compared to the objective-C code (see above question). I'm certainly open to other solutions and/or comments on this solution.

Convert [type_a] to [type_b]

I've actually googled this extensively, within stackoverflow and elsewhere.
Most questions are about [UInt8] to String or [UInt8] to type_a (not array).
To clarify, I'd like to take an array of type_a. Get its pointer and tell swift to treat the next n iterations of type_b (size_of) as array of type_b.
I've tried variations of https://stackoverflow.com/a/26954091/5276890 which didn't work. A comment there led me to https://stackoverflow.com/a/42255468/5276890.
withMemoryRebound seems like the right way but I couldn't find the right invocation.
Here's a sample code of what I'm doing instead to convert [UInt8] to [UInt32.bigEndian], both to clarify and in case it's useful (not likely)
var intData = [UInt32]()
let M = UInt32(256*256*256)
var m = M
var bigE:UInt32 = 0
for i in 0..<data.count {
bigE += UInt32(data[i]) * m
if m == 1 {
intData.append(bigE)
bigE = 0
m = M
} else {
m = m/256
}
}
<disclaimer+rant>
I have to admit I never could figure out the whole closures+withUnsafe* syntax and mostly used patterns online and modified them. I'd spend the time learning this, just as soon as the language authors decide and settle down on one specific syntax :(
</disclaimer+rant>
Use withUnsafeBufferPointer to get a pointer to the element
storage of the source array.
Use withMemoryRebound to "reinterpret" that pointer as pointing
to elements of the target type.
Use Array(UnsafeBufferPointer(...) to create an array of the
target type.
Example:
let source: [UInt16] = [1, 2, 3, 4]
let dest = source.withUnsafeBufferPointer {
$0.baseAddress!.withMemoryRebound(to: UInt32.self, capacity: 2) {
Array(UnsafeBufferPointer(start: $0, count: 2))
}
}
print(dest) // [131073, 262147]
Or as a generic function:
func convertArray<S, T>(_ source: [S], to: T.Type) -> [T] {
let count = source.count * MemoryLayout<S>.stride/MemoryLayout<T>.stride
return source.withUnsafeBufferPointer {
$0.baseAddress!.withMemoryRebound(to: T.self, capacity: count) {
Array(UnsafeBufferPointer(start: $0, count: count))
}
}
}
Example:
let source: [UInt16] = [1, 2, 3, 4]
let dest = convertArray(source, to: UInt32.self)
print(dest) // [131073, 262147]
If you only need a (temporary) view on the array storage interpreted
in another type then you can avoid the Array creation
and use the UnsafeBufferPointer (which is a Collection and
has array-like methods) without copying the data:
source.withUnsafeBufferPointer {
$0.baseAddress!.withMemoryRebound(to: UInt32.self, capacity: 2) {
let u32bufptr = UnsafeBufferPointer(start: $0, count: 2)
// ... Operate on u32bufptr ...
for elem in u32bufptr { print(elem) }
}
}

How to find object with smallest property value in a Swift object array?

I have the following Object Array for the Place class:
class Place: NSObject {
var distance:Double = Double()
init(_ distance: Double) {
self.distance = distance
}
}
let places = [Place(1.5), Place(8.4), Place(4.5)]
I need to get the Place with the minimum distance.
I tried using
let leastDistancePlace = places.min { $0.distance > $1.distance }
as per this answer for a similar question, but it gave the following error.
Contextual closure type '(Place) -> _' expects 1 argument, but 2 were
used in closure body
PS:
As per #robmayoff 's answer, I tried the following in a playground, but I keep getting an error:
value of Type [Place] no member min
Please check this screenshot.
My swift version is : Apple Swift version 2.2 (swiftlang-703.0.18.8 clang-703.0.31)
let leastDistancePlace = places.min { $0.distance < $1.distance }
or
let leastDistancePlace = places.min(by: { $0.distance < $1.distance })
Example:
:; xcrun swift
"crashlog" and "save_crashlog" command installed, use the "--help" option for detailed help
Welcome to Apple Swift version 3.0.2 (swiftlang-800.0.63 clang-800.0.42.1). Type :help for assistance.
1> class Place {
2. var distance:Double = Double()
3. init(_ distance: Double) {
4. self.distance = distance
5. }
6. }
7.
8.
9. let places = [Place(1.5), Place(8.4), Place(4.5)]
10. let leastDistancePlace = places.min { $0.distance < $1.distance }
places: [Place] = 3 values {
[0] = {
distance = 1.5
}
[1] = {
distance = 8.4000000000000004
}
[2] = {
distance = 4.5
}
}
leastDistancePlace: Place? = (distance = 1.5) {
distance = 1.5
}
11>
let sortedPlaces = places.sorted(by: { $0.distance < $1.distance })
let first = sortedPlace.first
just use sort
Your question is poorly worded however I think I know what you are trying to ask. The mapping function is generally used for tranformations:
let distances = places.map({ (place: Place) -> Int in
place.distance
})
For shorthand
let distances = places.map({ $0.distance })
You can then use max or min on this array of integers in order to extract the value you desire.

How to loop through array to find 4 identical values that are consecutive?

I have the following swift array:
var winSuitArray = [cardSuit1, cardSuit2, cardSuit3, cardSuit4, cardSuit5, cardSuit6, cardSuit7]
cardSuit1, cardSuit2 and so on, are variables that will equal strings like "clubs" or "hearts". What I want to do is loop through this array, and if the loop finds 4 identical objects whose indexes are consecutive, set the winSuitStatus bool to true.
For example, if the array looks like this:
["hearts", "clubs", "clubs", "clubs", "clubs", "diamonds", "spades"]
I want to loop through it like so:
for card in winSuitArray {
//find 4 identical and consecutive objects
// if the above requirements are met, let winSuitStatus = true
}
Is this possible to do?
To tell the truth, I'd probably do something similar to #KnightOfDragon's answer. There's nothing wrong with that approach. But this problem opens up some opportunities to build some much more reusable code at little cost, so it seems worth a little trouble to do that.
The basic problem is that you want to create a sliding window of a given size over the list, and then you want to know if any of the windows contain only a single value. So the first issue to to create these windows. We can do that very generally for all collections, and we can do it lazily so we don't have to compute all the windows (we might find our answer at the start of the list).
extension Collection {
func slidingWindow(length: Int) -> AnyRandomAccessCollection<SubSequence> {
guard length <= count else { return AnyRandomAccessCollection([]) }
let windows = sequence(first: (startIndex, index(startIndex, offsetBy: length)),
next: { (start, end) in
let nextStart = self.index(after: start)
let nextEnd = self.index(after: end)
guard nextEnd <= self.endIndex else { return nil }
return (nextStart, nextEnd)
})
return AnyRandomAccessCollection(
windows.lazy.map{ (start, end) in self[start..<end] }
)
}
}
The use of AnyRandomAccessCollection here is to just hide the lazy implementation detail. Otherwise we'd have to return a LazyMapSequence<UnfoldSequence<(Index, Index), ((Index, Index)?, Bool)>, SubSequence>, which would be kind of crazy.
Now are next question is whether all the elements in a window are equal. We can do that for any kind of Equatable sequence:
extension Sequence where Iterator.Element: Equatable {
func allEqual() -> Bool {
var g = makeIterator()
guard let f = g.next() else { return true }
return !contains { $0 != f }
}
}
And with those two pieces, we can just ask our question. In the windows of length 4, are there any runs that area all equal?
let didWin = suits.slidingWindow(length: 4).contains{ $0.allEqual() }
Or we could go a little different way, and create a SlidingWindowSequence that we could iterate over. The logic here is basically the same. This just wraps up the windowing into a specific type rather than a AnyRandomAccessCollection. This may be overkill for this problem, but it demonstrates another powerful pattern.
public struct SlidingWindowSequence<Base: Collection>: Sequence, IteratorProtocol {
let base: Base
let windowSize: Base.IndexDistance
private var windowStart: Base.Index
public init(_ base: Base, windowSize: Base.IndexDistance) {
self.base = base
self.windowSize = windowSize
self.windowStart = base.startIndex
}
public mutating func next() -> Base.SubSequence? {
if base.distance(from: windowStart, to: base.endIndex) < windowSize {
return nil
}
let window = base[windowStart..<base.index(windowStart, offsetBy: windowSize)]
windowStart = base.index(after: windowStart)
return window
}
}
let didWin = SlidingWindowSequence(suits, windowSize: 4).contains{ $0.allEqual() }
var suit = ""
var count = 1
for card in winSuitArray {
if(suit == card)
{
count++
}
else
{
count = 1
suit = card
}
if(count == 4)
{
//find 4 identical and consecutive objects
// if the above requirements are met, let winSuitStatus = true
}
}
You can use a counter variable to do this, initialized to 1.
for each value in array:
if value equals previous value
increment counter
else
counter = 1
if counter >= 4
set winCounter to true

Resources