In Swift, How to Write a Generic Function to Resize an Array

In Swift, can I write a generic function to resize an array?

The most generic method...

  1. Array adapts RangeReplaceableCollection protocol which include methods which can help resizing. (No need to use a loop)

  2. You need to construct new instances of the element when you grow the array. So you either provide a default value...

extension RangeReplaceableCollection {
public mutating func resize(_ size: IndexDistance, fillWith value: Iterator.Element) {
let c = count
if c < size {
append(contentsOf: repeatElement(value, count: c.distance(to: size)))
} else if c > size {
let newEnd = index(startIndex, offsetBy: size)
removeSubrange(newEnd ..< endIndex)
}
}
}

var f = ["a", "b"]
f.resize(5, fillWith: "") // ["a", "b", "", "", ""]
f.resize(1, fillWith: "") // ["a"]

  1. or you create a protocol that provides the default value init(). Note that you need to manually adapt the protocol to every types you care about.
public protocol DefaultConstructible {
init()
}

extension String: DefaultConstructible {}
extension Int: DefaultConstructible {}
// and so on...

extension RangeReplaceableCollection where Iterator.Element: DefaultConstructible {
public mutating func resize(_ size: IndexDistance) {
resize(size, fillWith: Iterator.Element())
}
}

var g = ["a", "b"]
g.resize(5)
g.resize(1)

Array of generic type as argument to function

You need to supply the generic type of the class Event. The following code compiles correctly:

Event<Int>.listenAny(l, events: v)

Note: I use Int as the generic type, but you can use any type.

Functions on generic arrays

You want to cast the tuple Array of type [(index: Int, value: T)] where T is of type Reminder to a tuple Array of type [(index: Int, reminder: Reminder)]. So you can see that the tuples have different element names (value and reminder) where both have different byte sizes - therefore the error.

So you should take the same element names for both tuples.

If you want to change the element names:

let newTupleArray = oldTupleArray.map{ (newElementName0: $0.0, newElementName1: $0.1) }

Functions on generic arrays

You want to cast the tuple Array of type [(index: Int, value: T)] where T is of type Reminder to a tuple Array of type [(index: Int, reminder: Reminder)]. So you can see that the tuples have different element names (value and reminder) where both have different byte sizes - therefore the error.

So you should take the same element names for both tuples.

If you want to change the element names:

let newTupleArray = oldTupleArray.map{ (newElementName0: $0.0, newElementName1: $0.1) }

Swift array of mixed generic types

No need to explicitly type:

class Test<T, U> {

init(key: T, value: U) {
}

}

let array: [Test<String, Any>] = [

Test(key: "test", value: []),
Test(key: "test", value: 42)
]

Update:

typealias tuple = (Any,Any)

class TestBlock
{
let key: String
let block: (tuple) -> Void

init(key: String, block: @escaping (tuple) -> Void)
{
self.key = key
self.block = block
}
}

let block1: (tuple) -> Void = { (arg) in

let (_label, _size) = arg
let label = _label as! UILabel
label.font = label.font.withSize((_size as! CGFloat))
}

let block2: (tuple) -> Void = { (arg) in

let (_label, _color) = arg
let label = _label as! UILabel
let color = _color as! UIColor
label.textColor = color
}

let propertiesWithBlock: [TestBlock] = [

TestBlock(key: "fontSize", block: block1),
TestBlock(key: "textColor", block: block2)
]

Swift generics and subclasses

One approach is to iterate over the array and use optional binding
to check if an element is of the given type:

func getFirst<T: MyClass>(ofType: T.Type) -> T? {
for elem in values {
if let item = elem as? T {
return item
}
}
return nil
}

This can be simplified using for case with the as pattern:

func getFirst<T: MyClass>(ofType: T.Type) -> T? {
for case let item as T in values {
return item
}
return nil
}

Another approach is to use flatMap to find all items of the given
type and return the first one:

func getFirst<T: MyClass>(ofType: T.Type) -> T? {
return values.flatMap { $0 as? T }.first
}

If the array can be large and you want to avoid the creation of an
intermediate array then you can use lazy:

func getFirst<T: MyClass>(ofType: T.Type) -> T? {
return values.lazy.flatMap { $0 as? T }.first
}

As an array extension method this would be

extension Array {
func getFirst<T>(ofType: T.Type) -> T? {
return flatMap { $0 as? T }.first
}
}

reserveCapacity(Swift) vs resize(C++)

You're confusing the underlying storage of the array from the actual size of the array.
Arrays in Swift are dynamic, so you can append() and the array will grow in size accordingly.
Internally, Swift manages how the underlying memory is allocated for you, so you don't need to think about it.

When you reserveCapacity, this is telling Swift you anticipate that the array will be of this size eventually so it should get the memory ready for you in advance.
It's essentially just a performance optimization that you don't normally need to make, unless you know an array will grow to a given size eventually.
This does not mean the array now has this memory initialized with values, so accessing an element out-of-bounds will cause a crash.

If you want an array that is pre-initialized with values, you can use the init(repeating:count:) initializer like so:

var arr = Array(repeating: 0, count: 10000)

This will both allocate storage for 10,000 elements and initialize each value to 0, so now you can access any index in this range safely.

Swift: Make function universal using generics?

Why you think you need generics here? Here how I see your function:

func open(inout theSwitch: Bool, array: [UIView]) {
for item in array {
item.hidden = !theSwitch
}
theSwitch = !theSwitch
}

Usage:

var theSwitch = true
var array = [UIButton]()
var mixedViewsArray = [UIButton(), UILabel(), UIView()]

// switch time:
open(&theSwitch, array: array)
open(&theSwitch, array: mixedViewsArray)

You don't need to use reference to original array, since you only change properties in objects in that array, so you only use references from it. open function will work with any subclass of UIView class, like UIButton or UITableViewCell (you don't even need cast mixedViewsArray to proper type, its already [UIView]).



Related Topics



Leave a reply



Submit