Chaining methods in Swift - swift

I'd like to write a function that I can chain to map { } functions. For example:
let arr = [1,2,3,4,5]
let list = arr.map { $0 * 2 }.applyRuleToSubtractVal(1) // return [1,3,5,7,9]
How do I define applyRuleToSubtractVal() above? (I want to do more complex things and don't want to do them inside the map.)

sketchyTech's answer is correct, but actually it is just hiding the fact, that
let list = arr.map { $0 * 2 }.map { $0 - 1 }
is executed — this is not optimal as the array is enumerated twice.
You could achieve the same with one enumeration with
let complexClosure = { (i: Int) in
return i-1
}
let list = arr.map { complexClosure($0 * 2) }

Your map returns an array of Int so you can apply any Array method that can be performed upon an Array<Int> using dot syntax. In order to include your own methods make sure that you extend Array in a way that makes this possible, e.g.
extension Array where Element:IntegerType {
func applyRuleToSubtractValue(num:Int) -> Array {
return self.map{$0 - 1}
}
}
let arr = [1,2,3,4,5]
let list = arr.map{$0 * 2}.applyRuleToSubtractValue(1) // [1, 3, 5, 7, 9]
If we didn't use protocol restrictions here then we wouldn't be able to perform a minus 1 operation, because it might be an array of any type.

You need to extend the Array's functionality. The dot operator calls methods of the class or struct. Extend your own functionality to the Array struct.

Related

In Swift, is there a builtin method for splitting an array into a prefix and a suffix?

Is there a builtin method on Swift Arrays which splits it into two pieces, preserving the order of all elements?
Something akin to Array.prefix and Array.suffix, combined into one?
I'm aware of partition and split, but they don't preserve order and size, respectively.
Example:
[1,2,3,5,6,2,3,5].cut(where: { $0 < 5 })
>>> ([1,2,3], [5,6,2,3,5])
I'm afraid there isn't such a function, which is a shame, because I've needed it several times now. It's pretty easy to roll your own, though:
extension RangeReplaceableCollection {
func cut(where belongsInFirstHalf: (Element) -> Bool) -> (SubSequence, SubSequence) {
guard let splittingIndex = self.firstIndex(where: { !belongsInFirstHalf($0) }) else {
return (self[...], SubSequence())
}
return (
self[..<splittingIndex],
self[splittingIndex...]
)
}
}
print([1,2,3,5,6,2,3,5].cut(where: { $0 < 5 })) // => (ArraySlice([1, 2, 3]), ArraySlice([5, 6, 2, 3, 5]))
Nothing built into Swift natively. My best suggestion would be to make an Array extension and do a findIndex passing in the where parameter, then splitting the Array based on that.

Is it possible to make array reference immutable, but array content mutable?

In Java, we can make an array reference immutable, and array content mutable, by using final keyword
Java
final int[] array = {1, 2, 3};
// Ok. Array content mutable.
array[0] = 9;
// Compiler error. Array reference immutable.
array = new int[]{4, 5, 6};
In Swift, they take one step further. Using let keyword, will make both array reference, and array content immutable.
Swift
let array = [1, 2, 3]
// Compiler error. Array content immutable.
array[0] = 9
// Compiler error. Array reference immutable.
array = [4, 5, 6]
In Swift, is it possible to make array reference immutable, but array content mutable?
The answer for your question is "yes" and "no", depends on what you have.
If you decide to declare a simple "let" constant, you can't modify it.
Why ? Because it prevents you to side effects (and you have some optimization).
For example if you just want to browse a list and print values, you don't modify the list.
myArray = [1,2,3]
for element in myArray {
print(element)
}
Why it can be cool ? Now if you know that you don't want to modify your list, it prevents you to use functions that can modify your list. It will save your time and avoid some behavior that you don't expect.
If you declare a var and you don't modify the value, Swift will tell you too.
Moreover, the concept of immutable in Swift is interesting if you use a struct or a class.
Imagine you have this structure and this class:
struct TestStruct {
var myInt: Int
init(myInt: Int) {
self.myInt = myInt
}
}
struct TestClass {
var myInt: Int
init(myInt: Int) {
self.myInt = myInt
}
}
In this structure you have myIntwhich is a var. What happens if you try to declare a TestStructure and a TestClass object with a let constant ?
let testStruct = Test(myInt: 3)
// Cannot assign to property: 'test' is a 'let' constant
test.myInt = 5
let testClass = Test(myInt: 3)
// It works
test.myInt = 5
In a struct, the let is propagated for every field, which is not the case for a class.
Using let keyword, will make both array reference, and array content immutable.
This isn't correct. There is no "array reference" here. An array is a value, just like an integer is a value. There is no "array reference." Variables can be let or var, but that doesn't change the nature of their value. You wouldn't say that var n = 4 made "4" mutable. Similarly, var ns = [1,2,3] doesn't make [1,2,3] mutable. It just means you can change what ns refers to. Calling ns.append(5) is just like n += 1. In each case they assign a new value. They don't mutate the old value.
As an implementation and optimization detail, it is possible that the underlying array storage that was used for ns will be mutated and used for the new ns value. But this is invisible to the caller. For example:
var array = [1,2] {
didSet { print("\(oldValue) -> \(array)") }
}
array.append(1)
array = [1,2,1]
// [1, 2] -> [1, 2, 1]
// [1, 2, 1] -> [1, 2, 1]
There's no deep difference between the append and the assignment. They are both assignments. And notice that setting the value to the same value is still just an assignment.
I'm harping on this because you can't just translate over a Java approach and have it work if your Java code relies on shared mutable state (where one part of the program modifies an array and others are supposed to have their reference update). But if your Java works that way, I recommend improving your Java to reduce its reliance on that. As long as you generally just pass values and return values, then it'll work exactly the same in Swift as in Java.
If you still need this kind of mutable array, then you can build one fairly easily by wrapping an Array in a class:
final class ArrayRef<Element>: MutableCollection, ExpressibleByArrayLiteral {
private var elements: [Element] = []
init(arrayLiteral elements: Element...) {
self.elements = elements
}
var startIndex: Int { elements.startIndex }
var endIndex: Int { elements.endIndex }
func index(after i: Int) -> Int { elements.index(after: i) }
subscript(position: Int) -> Element {
get { elements[position] }
set { elements[position] = newValue }
}
}
let array: ArrayRef = [1, 2, 3]
// Ok. "Array" content mutable.
array[0] = 9
// Compiler error. "Array" is immutable.
array = [4, 5, 6]
(This is a very simple and unoptimized implementation. With more work you can make it more efficient and improve the interface.)
But I don't particularly recommend this unless you really need it. There's a reason it doesn't exist in stdlib.

Swift 3: How to check the type of a generic array

I declare a generic array
fileprivate var array: [T?]
I have a method average(), which will calculate average if 'T' is Int or Float; otherwise returns 0
public func average() -> Float {
var mean = 0
if T is Int or Float {
for index in (0..<array.count-1){
mean = mean+array[index]
}
mean = mean/array.count
}
return mean;
}
Question: How will I check if array is holding Int/Float (if T is Int or Float, in above code)
This is a tool for protocols. The useful protocols for your problem are FloatingPoint to handle floating point types (like Float) and Integer to handle signed integer types (like Int). These have slightly different implementations, so it's best to write each one separately. Doing this will ensure that this method is only available for appropriate types of T (rather than all possible types, and just returning 0 in those cases).
extension MyStruct where T: FloatingPoint {
func average() -> T {
let sum = array.flatMap{$0}.reduce(0, +)
let count = T(array.count)
return sum.divided(by: count)
}
}
extension MyStruct where T: Integer {
func average() -> Float {
let sum = array.flatMap{$0}.reduce(0, +)
let count = array.count
return Float(sum.toIntMax()) / Float(count.toIntMax())
}
}
EDIT: Following up a bit more on Caleb's comments below, you may be tempted to think it's ok to just convert integers into floats to generate their average. But this is not safe in general without careful consideration of your ranges. For example, consider the average of [Int.min, Int.max]. That's [-9223372036854775808, 9223372036854775807]. The average of that should be -0.5, and that's what's returned by my example above. However, if you convert everything to floats in order to sum it, you'll get 0, because Float cannot express Int.max precisely. I've seen this bite people in live code when they do not remember that for very large floats, x == x+1.
Float(Int.max) == Float(Int.max - 1) // true
You can use the type method introduced in Swift 3 in the following way:
let type = type(of: array)
print("type: \(type)") // if T is String, you will see Array<Optional<String>> here
You will need to iterate over your array and use "if let" to unwrap the type of the values in the array. If they are ints handle them one way, if they are floats handle them another way.
//while iterating through your array check your elements to see if they are floats or ints.
if let stringArray = T as? Int {
// obj is a string array. Do something with stringArray
}
else {
// obj is not a string array
}
Here's a high level description:
... inside a loop which allows indexing
if let ex = array[index] as? Int {
your code
continue // go around the loop again, you're all done here
}
if let ex = array[index] as? Float {
// other code
continue // go around the loop again, you're all done here
}
// if you got here it isn't either of them
// code to handle that
... end of inside the loop
I can explain further if that isn't clear enough.
This is probably the simplest way to do it:
var average: Float {
let total = array.reduce(0.0) { (runningTotal, item) -> Float in
if let itemAsFloat = item as? Float {
return runningTotal + itemAsFloat
}
else if let itemAsInt = item as? Int {
return runningTotal + Float(itemAsInt)
}
else {
return runningTotal
}
}
return total / Float(array.count)
}
Obviously if you want it can be a function and depending on how you're wanting to use it you may need to tweak it.
*Note that it's possible to have both Int and Float in an array of T. e.g. if T is Any.

How to convert a Swift array to a tuple in a single line? [duplicate]

I just want to convert an array into a tuple in Swift; something like the following:
>>> myArray = [1,2,3,4,5]
>>> mytuple = tuple(myArray)
>>> mytuple
(1, 2, 3, 4, 5)
What's the easiest way of doing that?
(I only started learning Swift today, so I am very, very new).
It's actually quite possible, if you are willing to do some low level magic. I don't think it works if the tuple has a collection type, though. This is mainly for interacting with C libraries.
typealias TupleType = (UInt8, UInt8, UInt8)
var array = [2, 3, 4] as [UInt8]
var tuple = UnsafeMutablePointer<StepsType>(malloc(UInt(sizeof(TupleType))))
memcpy(tuple, array, UInt(array.count))
More of this stuff can be found here:
https://codereview.stackexchange.com/questions/84476/array-to-tuple-in-swift/84528#84528
Because 70% of us are highly visual beings:
You can't do this because the size of a tuple is part of its type, and this isn't true for an array. If you know the array's length, you can just do let myTuple = (myArray[0], myArray[1], ...). Or you can build your architecture around tuples from the beginning. What are you trying to do?
This is a common problem which occurs in lots of other languages. See How do I convert a list to a tuple in Haskell?
if what you want to do is extract multiple value from array at the same time, maybe
the following code could help you
extension Array {
func splat() -> (Element,Element) {
return (self[0],self[1])
}
func splat() -> (Element,Element,Element) {
return (self[0],self[1],self[2])
}
func splat() -> (Element,Element,Element,Element) {
return (self[0],self[1],self[2],self[3])
}
func splat() -> (Element,Element,Element,Element,Element) {
return (self[0],self[1],self[2],self[3],self[4])
}
}
then you can use it like this
let (first,_,third) = ( 0..<20 ).map { $0 }.splat()
you can even write a codegen to generate the extension code
This my universal solution for copy array data to tuple. The only problem that I could not solve is to check the type of the element in the tuple and the element from the array.
/// Unsafe copy array to tuple
func unsafeCopyToTuple<ElementType, Tuple>(array: inout Array<ElementType>, to tuple: inout Tuple) {
withUnsafeMutablePointer(to: &tuple) { pointer in
let bound = pointer.withMemoryRebound(to: ElementType.self, capacity: array.count) { $0 }
array.enumerated().forEach { (bound + $0.offset).pointee = $0.element }
}
}
Scared of unsafe pointers? Or need support for tuples with different types inside?
It can also be done like this:
public extension Array {
public var tuple: Any? {
switch count {
case 0:
return ()
case 1:
return (self[0])
case 2:
return (self[0], self[1])
case 3:
return (self[0], self[1], self[2])
case 4:
return (self[0], self[1], self[2], self[3])
case 5:
return (self[0], self[1], self[2], self[3], self[4])
default:
return nil
}
}
Because typing this out can be a little error prone, it's a good idea to write tests for this that make sure the right tuple is returned.
I've used this approach in ArrayPlusTuple.
So instead of writing this functionality yourself you could just add pod 'ArrayPlusTuple' to your pod file and get this tested functionality for free.

For-in loop and type casting only for objects which match type

I have seen answers here which explain how to tell the compiler that an array is of a certain type in a loop.
However, does Swift give a way so that the loop only loops over items of the specified type in the array rather than crashing or not executing the loop at all?
You can use a for-loop with a case-pattern:
for case let item as YourType in array {
// `item` has the type `YourType` here
// ...
}
This will execute the loop body only for those items in the
array which are of the type (or can be cast to) YourType.
Example (from
Loop through subview to check for empty UITextField - Swift):
for case let textField as UITextField in self.view.subviews {
if textField.text == "" {
// ...
}
}
Given an array like this
let things: [Any] = [1, "Hello", true, "World", 4, false]
you can also use a combination of flatMap and forEach fo iterate through the Int values
things
.flatMap { $0 as? Int }
.forEach { num in
print(num)
}
or
for num in things.flatMap({ $0 as? Int }) {
print(num)
}
In both cases you get the following output
// 1
// 4