I'm doing the A Swift Tour.
One:
I'm not getting the Generics functions logic. Its for Design Patterns? And the explanation on the tour looks very short and unclear.
Two:
In this pice of code for creating a generic function,
func repeatItem<Item>(item: Item, numberOfTimes: Int) -> [Item] {
var result = [Item]()
for _ in 0..<numberOfTimes { //im not getting this line (?)
result.append(item)
}
return result
}
repeatItem("knock", numberOfTimes:4)
I do not understand this syntax very well, what means _, .., and < in the same line, why is used?
_, .., and < are not part of the generics.
_ is just an non-name for a parameter that is never used.
usually you would put a variable name like i there and use it in the block but as you are just doing something a certain number of times you are not really using the index.
..< is a shorthand for the range between the start value and the end value. 1..<5 would then generate the range 1,2,3,4
there is also a range shorthand ... that gives you the last value 5
Generic code enables you to write flexible, reusable functions and
types that can work with any type, subject to requirements that you
define. You can write code that avoids duplication and expresses its
intent in a clear, abstracted manner.
Generics
When you don't use a variable anywhere else, you can use an underscore. So if you're looping over some range of numbers, and you're not using the index, then you can use a _. It's just syntax sugar.
The 0..<4 means means start counting at 0, and count up to 4-1=3. You can include the last number using 0...4, which would mean *start counting at 0, and count up to 4`.
for i in 0..<4 {
print(i)
} // prints 0, 1, 2, 3
While,
for i in 0...4 {
print(i)
} // prints 0, 1, 2, 3, 4
Consider the old school for loop in C:
for var i = 0; i < numberOfTimes; i++ {
result.append(item)
}
Yuck, a lot of code just to repeat result.append(item) for numberOfTimes! Swift has a shorthand syntax: a..<b means "iterate from a to less than b" so your for loop can be rewritten as:
for i in 0..<numberOfTimes {
result.append(item)
}
But then you don't use i inside the body of the loop either. All you want is to repeat it numberOfTimes. So you don't care what name the iterator takes: i, x or z. Hence you don't even need to declare it, just replace it with a _:
for _ in 0..<numberOfTimes {
result.append(item)
}
for _ in 0..<numberOfTimes {
(something)
}
Is equivalent to:
for(var nameForWhichIdontCare = 0; nameForWhichIDontCare < numberOfTimes {
(something)
}
So:
_ is used in places where a name is required but you dont care, so basically discarding the name from the beginning.
..< is a range up to a number.
... is a range up to and including a number.
Related
I want to compare two Arrays with each other, that means each single item of them.
I need to run some code if two items in this arrays are the same.
I've done that so far with two For-Loops, but someone in the comments says that's not that good (because I get an Error too.
Has anybody an Idea which Code I can use to reach that?
Btw: That's the code I used before:
var index = 0
for Item1 in Array1 {
for Item2 in Array2 {
if (Item1 == Item2) {
// Here I want to put some code if the items are the same
}
}
// Index is in this case the counter which counts on which place in the Array1 I am.
index += 1
}
Okay I'll try again to describe what I mean:
I want to compare two Arrays. If there are some Items the same, I want to delete that Item from Array A / Array 1.
Okay if that is working, I want to add a few more statements that alow me to delete a Item only if an parameter of this item has a special worth, but I think I can do this step alone.
If you want to compare items from different array you need to add Equatable protocol for your Item
For example:
struct Item: Equatable {
let name: String
static func ==(l: Item, r: Item) -> Bool {
return l.name == r.name
}
}
You need to decide by which attributes you want to compare your Item. In my case I compare by name.
let array1: [Item] = [
.init(name: "John"),
.init(name: "Jack"),
.init(name: "Soer"),
.init(name: "Kate")
]
let array2: [Item] = [
.init(name: "John"),
]
for item1 in array1 {
if array2.contains(item1) {
// Array 2 contains item from the array1 and you can perform your code.
}
}
If you want to support this
Okay I'll try again to describe what I mean: I want to compare two
Arrays. If there are some Items the same, I want to delete that Item
from Array A / Array 1. Okay if that is working, I want to add a few
more statements that alow me to delete a Item only if an parameter of
this item has a special worth, but I think I can do this step alone.
I guess it can fit for you
You need to make your array1 mutable
var array1: [Item] = ...
Filter the array1 like this
let filteredArray1 = array1.filter { (item) -> Bool in
return !array2.contains(item)
}
And redefine your array1 with filtered array.
array1 = filteredArray1
Let me know it it works for you.
var itemsToRemove = array1.filter { array2.contains($0) }
for item in itemsToRemove {
if let i = array1.firstIndex(of: item) {
// run your conditional code here.
array1.remove(at: i)
}
}
Update
The question has been restated that elements are to be removed from one of the arrays.
You don't provide the actual code where you get the index out of bounds error, but it's almost certainly because you don't account for having removed elements when using the index, so you're probably indexing into a region that was valid at the start, but isn't anymore.
My first advice is don't do the actual removal inside the search loop. There is a solution to achieve the same result, which I'll give, but apart from having to be very careful about indexing into the shortened array, there is also a performance issue: Every deletion requires Array to shift all the later elements down one. That means that every deletion is an O(n) operation, which makes an the overall algorithim O(n^3).
So what do you do instead? The simplest method is to create a new array containing only the elements you wish to keep. For example, let's say you want to remove from array1 all elements that are also in array2:
array1 = array1.filter { !array2.contains($0) }
I should note that one of the reasons I kept my original answer below is because you can use those methods to replace array2.contains($0) to achieve better performance in some cases, and the original answer describes those cases and how to achieve it.
Also even though the closure is used to determine whether or not to keep the element, so it has to return a Bool, there is nothing that prevents you from putting additional code in it to do any other work you might want to:
array1 = array1.filter
{
if array2.contains($0)
{
doSomething(with: $0)
return false // don't keep this element
}
return true
}
The same applies to all of the closures below.
You could just use the removeAll(where:) method of Array.
array1.removeAll { array2.contains($0) }
In this case, if the closure returns true, it means "remove this element" which is the opposite sense of the closure used in filter, so you have to take that into account if you do additional work in it.
I haven't looked up how the Swift library implements removeAll(where:) but I'm assuming it does its job the efficient way rather than the naive way. If you find the performance isn't all that good you could roll your own version.
extension Array where Element: Equatable
{
mutating func efficientRemoveAll(where condition: (Element) -> Bool)
{
guard count > 0 else { return }
var i = startIndex
var iEnd = endIndex
while i < iEnd
{
if condition(self[i])
{
iEnd -= 1
swapAt(i, iEnd)
continue // note: skips incrementing i,iEnd has been decremented
}
i += 1
}
removeLast(endIndex - iEnd)
}
}
array1.efficientRemoveAll { array2.contains($0) }
Instead of actually removing the elements inside the loop, this works by swapping them with the end element, and handling when to increment appropriately. This collects the elements to be removed at the end, where they can be removed in one go after the loop finishes. So the deletion is just one O(n) pass at the end, which avoids increasing the algorithmic complexity that removing inside the loop would entail.
Because of the swapping with the current "end" element, this method doesn't preserve the relative order of the elements in the mutating array.
If you want to preserve the order you can do it like this:
extension Array where Element: Equatable
{
mutating func orderPreservingRemoveAll(where condition: (Element) -> Bool)
{
guard count > 0 else { return }
var i = startIndex
var j = i
repeat
{
swapAt(i, j)
if !condition(self[i]) { i += 1 }
j += 1
} while j != endIndex
removeLast(endIndex - i)
}
}
array1.orderPreservingRemoveAll { array2.contains($0) }
If I had to make a bet, this would be very close to how standard Swift's removeAll(where:) for Array is implemented. It keeps two indices, one for the current last element to be kept, i, and one for the next element to be examined, j. This has the effect of accumulating elements to be removed at the end (those past i).
Original answer
The previous solutions are fine for small (yet still surprisingly large) arrays, but they are O(n^2) solutions.
If you're not mutating the arrays, they can be expressed more succinctly
array1.filter { array2.contains($0) }.forEach { doSomething($0) }
where doSomething doesn't actually have to be a function call - just do whatever you want to do with $0 (the common element).
You'll normally get better performance by putting the smaller array inside the filter.
Special cases for sorted arrays
If one of your arrays is sorted, then you might get better performance in a binary search instead of contains, though that will require that your elements conform to Comparable. There isn't a binary search in the Swift Standard Library, and I also couldn't find one in the new swift-algorithms package, so you'd need to implement it yourself:
extension RandomAccessCollection where Element: Comparable, Index == Int
{
func sortedContains(_ element: Element) -> Bool
{
var range = self[...]
while range.count > 0
{
let midPoint = (range.startIndex + range.endIndex) / 2
if range[midPoint] == element { return true }
range = range[midPoint] > element
? self[..<midPoint]
: self[index(after: midPoint)...]
}
return false
}
}
unsortedArray.filter { sortedArray.sortedContains($0) }.forEach { doSomething($0) }
This will give O(n log n) performance. However, you should test it for your actual use case if you really need performance, because binary search is not especially friendly for the CPU's branch predictor, and it doesn't take that many mispredictions to result in slower performance than just doing a linear search.
If both arrays are sorted, you can get even better performance by exploiting that, though again you have to implement the algorithm because it's not supplied by the Swift Standard Library.
extension Array where Element: Comparable
{
// Assumes no duplicates
func sortedIntersection(_ sortedOther: Self) -> Self
{
guard self.count > 0, sortedOther.count > 0 else { return [] }
var common = Self()
common.reserveCapacity(Swift.min(count, sortedOther.count))
var i = self.startIndex
var j = sortedOther.startIndex
var selfValue = self[i]
var otherValue = sortedOther[j]
while true
{
if selfValue == otherValue
{
common.append(selfValue)
i += 1
j += 1
if i == self.endIndex || j == sortedOther.endIndex { break }
selfValue = self[i]
otherValue = sortedOther[j]
continue
}
if selfValue < otherValue
{
i += 1
if i == self.endIndex { break }
selfValue = self[i]
continue
}
j += 1
if j == sortedOther.endIndex { break }
otherValue = sortedOther[j]
}
return common
}
}
array1.sortedIntersection(array2).forEach { doSomething($0) }
This is O(n) and is the most efficient of any of the solutions I'm including, because it makes exactly one pass through both arrays. That's only possible because both of the arrays are sorted.
Special case for large array of Hashable elements
However, if your arrays meet some criteria, you can get O(n) performance by going through Set. Specifically if
The arrays are large
The array elements conform to Hashable
You're not mutating the arrays
Make the larger of the two arrays a Set:
let largeSet = Set(largeArray)
smallArray.filter { largeSet.contains($0) }.forEach { doSomething($0) }
For the sake of analysis if we assume both arrays have n elements, the initializer for the Set will be O(n). The intersection will involve testing each element of the smallArray's elements for membership in largeSet. Each one of those tests is O(1) because Swift implements Set as a hash table. There will be n of those tests, so that's O(n). Then the worst case for forEach will be O(n). So O(n) overall.
There is, of course, overhead in creating the Set, the worst of which is the memory allocation involved, so it's not worth it for small arrays.
While reviewing some code, I found a Rank enum implemented as:
enum Rank: String {
case first
case second
case third
}
However, the surprising part for me was that I saw a code similar to this:
let gold = [300, 200, 100]
let firstPrize = gold[Rank.first.hashValue] // 300
means that Rank.first.hashValue has been used as an index! For the first look, it seems to be not a good idea to use a hash value as an index for an array:
Hash values are not guaranteed to be equal across different executions
of your program. Do not save hash values to use during a future
execution.
hashValue
Nevertheless it never causes an issue (at least that's what they said).
I tried to trace the issue, by implementing:
print(Rank.first.hashValue) // 0
print(Rank.second.hashValue) // 1
print(Rank.third.hashValue) // 2
and I saw is the output is always the same.
Although we could declare a property in the enum to do such a functionality, as:
var index: Int {
switch self {
case .first:
return 0
case .second:
return 1
case .third:
return 2
}
}
hence:
let firstPrize = gold[Rank.first.index] // 300
I would prefer to know why using the hashValue seems to be ok in this case? Could it be related to: I misunderstand what exactly is hashValue?
I am pretty much new to swift so, please bear with me :)
what if, in the following code
for i in (1...(self.count - 1)) { //(self.count is number of elements in Array Extension)
print(i)
}
self.count becomes 1.
well.. i can always work like the following
if self.count > 1
{
for i in (1...(self.count - 1)) {
print(i)
}
}
else
{
for i in ((self.count - 1)...1) {
print(i)
}
}
but is there another (probably better) way to deal with this ?
in which i won't have to use if-else control statements
PS :- as suggested in the comments, I have also used
for i in [(1...(self.count - 1))]
{
print(i)
}
but it still crashes when self.count = 1
To iterate over all array indices, use the ..< range operator which
omits the upper bound:
for i in 0 ..< array.count { }
or better:
for i in array.indices { }
The latter has the advantage that it works also for collections with
indices which are not zero-based (such as ArraySlice).
To iterate over all indices but the first,
for i in array.indices.dropFirst() { }
works in all cases, even if the array is empty. You should also check
if you really need the indices, or if you actually want to iterate
over the array elements, such as
for elem in array.dropFirst() { }
The best way to deal with range exceptions is to just not cause them
in the first place ;)
Why not just use a zero based index and then adjust it later in the loop if needed?
extension Array {
func oneBasedIndices() -> [Int] {
return (0..<count).map { return $0 + 1 }
}
}
The smallest number Array's count member can ever be is 0 and 0..<0 will never fail, so maybe something like this extension would do the trick if what you want is 1 based indices.
I have a small debate at work: Is it a good practice to calculate the size of the Array in swift before running over it's items? What would be a better code practice:
Option A:
func setAllToFalse() {
for (var i = 0; i < mKeyboardTypesArray.count; i++ ) {
self.mKeyboardTypesArray[i] = false
}
}
or Option B:
func setAllToFalse() {
let typesCount = mKeyboardTypesArray.count
for (var i = 0; i < typesCount; i++ ) {
self.mKeyboardTypesArray[i] = false
}
}
All, of course, if I don't alter the Array during the loops.
I did go over the documentation, which states this:
The loop is executed as follows:
When the loop is first entered, the initialization expression is
evaluated once, to set up any constants or variables that are needed
for the loop. The condition expression is evaluated. If it evaluates
to false, the loop ends, and code execution continues after the for
loop’s closing brace (}). If the expression evaluates to true, code
execution continues by executing the statements inside the braces.
After all statements are executed, the increment expression is
evaluated. It might increase or decrease the value of a counter, or
set one of the initialized variables to a new value based on the
outcome of the statements. After the increment expression has been
evaluated, execution returns to step 2, and the condition expression
is evaluated again.
The idiomatic way to say this in Swift is:
func setAllToFalse() {
mKeyboardTypesArray = mKeyboardTypesArray.map {_ in false}
}
That way, there is nothing to evaluate and nothing to count.
In fact, this would make a nice Array method:
extension Array {
mutating func setAllTo(newValue:T) {
self = self.map {_ in newValue}
}
}
Now you can just say:
mKeyboardTypesArray.setAllTo(false)
Alternatively, you could do it this way (this involves taking the count, but only once):
mKeyboardTypesArray = Array(count:mKeyboardTypesArray.count, repeatedValue:false)
The loop condition is evaluated each time through the loop. Consider this experiment:
extension Array {
var myCount: Int {
get {
println("here")
return self.count
}
}
}
let a = [1, 2, 3, 4, 5]
for var i = 0; i < a.myCount; i++ {
println(a[i])
}
Output:
here 1 here 2 here 3 here 4 here 5 here
You could see a small speed improvement from Option B, but I expect that the count property on Array is not expensive if the array is unchanged. It is potentially a good code practice anyway because it communicates to the reader that you expect the array size to remain constant for duration of the loop.
It is possible that the compiler would optimize array.count by detecting that nothing in the loop modifies the array, but it wouldn't be able to do that for array.myCount because of the println side effect.
I have found that it is not, which can cause a crash if you're iterating through an array and removing items (for example). In current Swift syntax, this for loop
for i in 0..<m_pendingCommands.count
{
if m_pendingCommands[i].packetID < command.packetID
{
m_pendingCommands.remove(at: i)
}
}
crashed halfway through the array, with a bad index.
I switched this to a while loop:
var i: Int = 0
while i < m_pendingCommands.count
{
if m_pendingCommands[i].packetID < ID
{
m_pendingCommands.remove(at: i)
}
else
{
i += 1
}
}
Let's say I have function which returns optional. nil if error and value if success:
func foo() -> Bar? { ... }
I can use following code to work with this function:
let fooResultOpt = foo()
if let fooResult = fooResultOpt {
// continue correct operations here
} else {
// handle error
}
However there are few problems with this approach for any non-trivial code:
Error handling performed in the end and it's easy to miss something. It's much better, when error handling code follows function call.
Correct operations code is indented by one level. If we have another function to call, we have to indent one more time.
With C one usually could write something like this:
Bar *fooResult = foo();
if (fooResult == null) {
// handle error and return
}
// continue correct operations here
I found two ways to achieve similar code style with Swift, but I don't like either.
let fooResultOpt = foo()
if fooResult == nil {
// handle error and return
}
// use fooResultOpt! from here
let fooResult = fooResultOpt! // or define another variable
If I'll write "!" everywhere, it just looks bad for my taste. I could introduce another variable, but that doesn't look good either. Ideally I would like to see the following:
if !let fooResult = foo() {
// handle error and return
}
// fooResult has Bar type and can be used in the top level
Did I miss something in the specification or is there some another way to write good looking Swift code?
Your assumptions are correct—there isn't a "negated if-let" syntax in Swift.
I suspect one reason for that might be grammar integrity. Throughout Swift (and commonly in other C-inspired languages), if you have a statement that can bind local symbols (i.e. name new variables and give them values) and that can have a block body (e.g. if, while, for), those bindings are scoped to said block. Letting a block statement bind symbols to its enclosing scope instead would be inconsistent.
It's still a reasonable thing to think about, though — I'd recommend filing a bug and seeing what Apple does about it.
This is what pattern matching is all about, and is the tool meant for this job:
let x: String? = "Yes"
switch x {
case .Some(let value):
println("I have a value: \(value)")
case .None:
println("I'm empty")
}
The if-let form is just a convenience for when you don't need both legs.
If what you are writing is a set of functions performing the same sequence of transformation, such as when processing a result returned by a REST call (check for response not nil, check status, check for app/server error, parse response, etc.), what I would do is create a pipeline that at each steps transforms the input data, and at the end returns either nil or a transformed result of a certain type.
I chose the >>> custom operator, that visually indicates the data flow, but of course feel free to choose your own:
infix operator >>> { associativity left }
func >>> <T, V> (params: T?, next: T -> V?) -> V? {
if let params = params {
return next(params)
}
return nil
}
The operator is a function that receives as input a value of a certain type, and a closure that transforms the value into a value of another type. If the value is not nil, the function invokes the closure, passing the value, and returns its return value. If the value is nil, then the operator returns nil.
An example is probably needed, so let's suppose I have an array of integers, and I want to perform the following operations in sequence:
sum all elements of the array
calculate the power of 2
divide by 5 and return the integer part and the remainder
sum the above 2 numbers together
These are the 4 functions:
func sumArray(array: [Int]?) -> Int? {
if let array = array {
return array.reduce(0, combine: +)
}
return nil
}
func powerOf2(num: Int?) -> Int? {
if let num = num {
return num * num
}
return nil
}
func module5(num: Int?) -> (Int, Int)? {
if let num = num {
return (num / 5, num % 5)
}
return nil
}
func sum(params: (num1: Int, num2: Int)?) -> Int? {
if let params = params {
return params.num1 + params.num2
}
return nil
}
and this is how I would use:
let res: Int? = [1, 2, 3] >>> sumArray >>> powerOf2 >>> module5 >>> sum
The result of this expression is either nil or a value of the type as defined in the last function of the pipeline, which in the above example is an Int.
If you need to do better error handling, you can define an enum like this:
enum Result<T> {
case Value(T)
case Error(MyErrorType)
}
and replace all optionals in the above functions with Result<T>, returning Result.Error() instead of nil.
I've found a way that looks better than alternatives, but it uses language features in unrecommended way.
Example using code from the question:
let fooResult: Bar! = foo();
if fooResult == nil {
// handle error and return
}
// continue correct operations here
fooResult might be used as normal variable and it's not needed to use "?" or "!" suffixes.
Apple documentation says:
Implicitly unwrapped optionals are useful when an optional’s value is confirmed to exist immediately after the optional is first defined and can definitely be assumed to exist at every point thereafter. The primary use of implicitly unwrapped optionals in Swift is during class initialization, as described in Unowned References and Implicitly Unwrapped Optional Properties.
How about the following:
func foo(i:Int) ->Int? {
switch i {
case 0: return 0
case 1: return 1
default: return nil
}
}
var error:Int {
println("Error")
return 99
}
for i in 0...2 {
var bob:Int = foo(i) ?? error
println("\(i) produces \(bob)")
}
Results in the following output:
0 produces 0
1 produces 1
Error
2 produces 99