How to sort structures by property in Swift [duplicate] - swift

This question already has answers here:
How to sort an array of custom objects by property value in Swift
(20 answers)
Closed 7 years ago.
I am trying to sort an array of structure depending on its property.
Lets say I want to sort an array of NSViews by the x coordinate.
How can I achive this?

Its a very pretty solution for that, and its called Closure Expression Syntax.
What you need to do is:
let sortedArray = sorted(allViewsArray, { (p1: NSView, p2: NSView) -> Bool in
return p1.frame.origin.x < p2.frame.origin.x
})
This will sort the alLViewsArray from the biggest X coordinate to the smallest, and store it in sortedArary.

Note, you can simplify the syntax a little, which often helps with readability (focus is on what you’re doing rather than the syntax of the types etc):
let sortedArray = sorted(allViewsArray) {
$0.frame.origin.x < $1.frame.origin.x
}
Trailing closures can be outside the function call parens, resembling other block structures like if or while; you can skip the return if the closure expression is a single statement, and you can skip the function signature and use $0, $1 etc. for the argument names.
That last one is best used only when there are no more useful names to be had (e.g. p1 is no more descriptive than $0). If you do want to give them names, you can still skip the types:
let sortedArray = sorted(allViewsArray) { p1, p2 in
p1.frame.origin.x < p2.frame.origin.x
}
Swift is sometimes a little fragile when applying this syntax sugar so occasionally you’ll find it can’t be shortened quite as much as it ought, but it usually works.

Related

Swift - should I create local variable of a strings "count"?

Does it matter if I use a strings 'count' multiple times within a function. That is, does Swift cache the 'count' after it firsts computes it. Below are two examples, does it matter which one I use? I assume the second is definitely okay but what about the first? I see example code like the first one all the time.
func Foo1 (str: String) {
...
// calling str.count twice
if x < str.count && y < str.count {
...
}
func Foo2 (str: String) {
...
// calling str.count once
let c = str.count
if x < c && y < c {
...
}
.count is defined by the Collection protocol with the following complexity:
Complexity: O(1) if the collection conforms to RandomAccessCollection; otherwise, O(n), where n is the length of the collection.
String is not a RandomAccessCollection. It's a BidirectionalCollection, so it does not promise O(1). It only promises O(n).
It definitely does not promise any caching (and you shouldn't expect any).
It happens to be true that in many (probably most) cases, String's count is cached. It's part of _StringObject, which is part of the low-level storage abstraction, and it's often inlined by the optimizer. But none of this is promised.
That said, unless you expect the String to be extremely large (10kB at a minimum, possibly more), it is difficult to imagine this being a major bottleneck by being called twice outside a tight loop. As with most things, you should write clearly, and then profile. I would likely create an extra variable just for clarity, but you shouldn't second-guess here too much. Write clearly. Then profile.
Do you have particularly large strings that you're working with?

Iterating through all but the last index of an array

I understand that in Swift 3 there have been some changes from typical C Style for-loops. I've been working around it, but it seems like I'm writing more code than before in many cases. Maybe someone can steer me in the right direction because this is what I want:
let names : [String] = ["Jim", "Jenny", "Earl"]
for var i = 0; i < names.count - 1; i+=1 {
NSLog("%# loves %#", names[i], names[i+1])
}
Pretty simple stuff. I like to be able to get the index I'm on, and I like the for-loop to not run if names.count == 0. All in one go.
But it seems like my options in Swift 3 aren't allowing me this. I would have to do something like:
let names : [String] = ["Jim", "Jenny", "Earl"]
if names.count > 0 {
for i in 0...(names.count - 1) {
NSLog("%# loves %#", names[i], names[i+1])
}
}
The if statement at the start is needed because my program will crash in the situation where it reads: for i in 0...0 { }
I also like the idea of being able to just iterate through everything without explicitly setting the index:
// Pseudocode
for name in names.exceptLastOne {
NSLog("%# loves %#", name, name.plus(1))
}
I feel like there is some sort of syntax that mixes all my wants, but I haven't come across it yet. Does anyone know of a way? Or at least a way to make my code more compact?
UPDATE: Someone suggested that this question has already been asked, citing a SO post where the solution was to use something to the degree of:
for (index, name) in names.enumerated {}
The problem with this when compared to Hamish's answer is that I only am given the index of the current name. That doesn't allow me to get the value at index without needing to do something like:
names[index + 1]
That's just one extra variable to keep track of. I prefer Hamish's which is:
for i in names.indices.dropLast() {
print("\(names[i]) loves \(names[i + 1])")
}
Short, simple, and only have to keep track of names and i, rather than names, index, and name.
One option would be to use dropLast() on the array's indices, allowing you to iterate over a CountableRange of all but the last index of the array.
let names = ["Jim", "Jenny", "Earl"]
for i in names.indices.dropLast() {
print("\(names[i]) loves \(names[i + 1])")
}
If the array has less than two elements, the loop won't be entered.
Another option would be to zip the array with the array where the first element has been dropped, allowing you to iterate through the pairs of elements with their successor elements:
for (nameA, nameB) in zip(names, names.dropFirst()) {
print("\(nameA) loves \(nameB)")
}
This takes advantage of the fact that zip truncates the longer of the two sequences if they aren't of equal length. Therefore if the array has less than two elements, again, the loop won't be entered.

Swift: Can someone explain this syntax `numbers.sort { $0 > $1 }` for me?

First of all, this question is not about "what does $0 mean". I learnt in swift document that $0 is like index.
My question is "How numbers.sort { $0 > $1 } can be used to implement a sort function". I searched for this syntax numbers.sort { $0 > $1 } in some other websites, for example this one. It is apparently not the current version. So I still can't understand what the meaning of it.
print(numbers) //[20, 19, 1, 12]
let sortedNumbers = numbers.sort { $0 > $1 }
print(sortedNumbers) //[20, 19, 12, 1]
Can someone explain this simple piece of code above for me? Like how this simple code $0 > $1 implement the sort function, sorting the numbers from big to small.
I know some about index, and this $0 looks like index, but it only has $0 and $1 two indices. So how can it be used into 4 numbers? According to my knowledge in C++ before, I can't understand the principle in this.
Please make your answer as specific as possible. Thank you!
----------------- Below is edited extra part -------------------
I don't know whether stackoverflow would allow me to edit my question like this, but this extra part is too long, so I can't add it in the comment.
#pbodsk #Paul Richter
So the sort() syntax in swift uses quick sort to deal with sort function?
Actually my question is more about "what is the operating principle of sort{$0 > $1}". I know what you mean above, and I think it's similar with what swift 2.1 document says, but your answer is not what I really want to know. Sorry, my English expression is not very good. Let me try another way.
When I learnt C++ before, there are always some documents to explain what a function's operating principle is or how this function (like sort() here) operate in background. Sort() here needs to compare first and second interchange. In C++, it's like
if numbers[1] < numbers[2]{ //just consider this pseudocode
int k;
k = numbers[1];
numbers[1] = numbers[2];
numbers[2] = k;
}
We can see this process is obvious. In swift, it's like
numbers.sort({(val1: Int, val2: Int) -> Bool in
return val1 > val2
})
Where is it compared? And how is it interchanged? Does return val1 > val2 automatically compare and interchange these two values and return them? Just this one syntax implement these all 3 processes? How? This is what I really want to know. Sorry again for my poor English expression.
#the_UB and #moonvader are both right, but I just thought that I would extend the example from #moonvader a bit, just to show you how we end up with $0 > $1
If you look at the example in "The Swift Programming Language" about Closure Expressions you can see that to sort an array you call the sort method which can then take a function as a parameter.
This function must take two parameters and compare them, and then return a boolean.
So if we have this array:
let numbers = [4, 6, 8, 1, 3]
and this method
func sortBackwards(val1: Int, val2: Int) -> Bool {
print("val1: \(val1) - val2: \(val2)" )
return val1 > val2
}
We can sort the elements like so:
numbers.sort(sortBackwards) //gives us [8, 6, 4, 3, 1]
The sort method will use our sortBackwards method on each of the elements in the array and compare them.
Here's the output of the print
val1: 6 - val2: 4
val1: 8 - val2: 4
val1: 8 - val2: 6
val1: 1 - val2: 4
val1: 3 - val2: 1
val1: 3 - val2: 4
OK, let's reduce that.
Instead of defining a function, we can add that directly as a parameter to the sort method like so:
numbers.sort({(val1: Int, val2: Int) -> Bool in
return val1 > val2
})
And we still end up with [8, 6, 4, 3, 1] (how fortunate!)
OK, the next thing we can do is what in "The Swift Programming Language" (the link above) is called "Infering Type From Context". As we call this method on an array of Ints, Swift can figure out that our val1 and val2 parameters must be Ints too, there's no need for us to tell it. So, lets remove the types. That leaves us with:
numbers.sort({val1, val2 in
return val1 > val2
})
And still the same result.
OK, getting there. The next thing we can do is what in the book is called "Implicit Returns from Single-Expression Closures"
As our comparison can be done in one line there's no need for us to use return. So:
numbers.sort({val1, val2 in val1 > val2})
Still gives us [8, 6, 4, 3, 1]
Finally we're getting to what #moonvader used much much less words to explain :-) namely "Shorthand Argument Names"
As it says in the book:
Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names $0, $1, $2, and so on.
So, in our example, val1 can be replaced by $0 and val2 can be replaced by $1
Which gives us:
numbers.sort({$0 > $1})
And still we get [8, 6, 4, 3, 1]
We can then continue to use a "Trailing Closure", which means that if the last parameter of a function is a closure, we can add that parameter "outside" the function.
So we end up with:
numbers.sort{$0 > $1}
And the outcome is still [8, 6, 4, 3, 1]
Hope that helps to clarify things.
Here is what all need to know: Sort and Sorted.
To be more specific, Sorting can be two type : Ascending and Descending.
Q - So to do sorting, what do we need?
A - We need two variables to hold two variable(I don't know if it is the correct word)
Hence in this case we have two variable $0 and $1. These both are shorthands to represent left and right variable. Both will help to sort.
">" will do descending.
"<" will do ascending.
From developer.apple.com
Shorthand Argument Names
Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names $0, $1, $2, and so on.
If you use these shorthand argument names within your closure expression, you can omit the closure’s argument list from its definition, and the number and type of the shorthand argument names will be inferred from the expected function type. The in keyword can also be omitted, because the closure expression is made up entirely of its body:
reversed = names.sort( { $0 > $1 } )
Here, $0 and $1 refer to the closure’s first and second String arguments.
The process of sorting a list consists of repeatedly reordering its elements until nothing remains to be reordered. Now there are many sorting algorithms, but they all do this, in different ways. So then how are elements reordered? By comparing two given elements, and deciding which comes first, and swapping them if needed.
We can separate the overall reordering and swapping parts from the comparison part, and write a sort function that will take care of all the repeated reordering stuff, and just require the caller to specify how to compare two elements. If the list consists of numbers, it's almost always the case that the way to compare them is to just take their value. But suppose the list consists of things a little more complicated, like cars. How do you compare two cars? Well, you could compare them by numerically comparing their top speed. Or their gas mileage. Or price.
But the comparison doesn't have to be numerical. We could compare two cars by actually racing them. We could compare two cars by just saying if one is blue and the other isn't, the blue one is ordered first, and if neither or both are blue they are ordered as they already are.
We could come up with all sorts of ways to compare two cars. And the sorting algorithm could then sort a list of cars, without knowing anything about cars, as long as we the caller just tell it how to compare cars - any two given cars. We just have to express that comparison as an expression that returns a boolean, where if it's true, the first car is ordered before the second one, and if it's false, the first car is ordered after the second one.
Returning to numbers, that's what sort { $0 > $1 } means, in Swift's very concise syntax: "Sort, where if the first element is > the second one, order the first one before the second one."
You asked how it can sort four numbers with only two indices. $0 and $1 are not bound to the four specific elements in the list [20, 19, 1, 12], they are bound to any two given numbers that need to be compared, because the sorting algorithm repeately needs to do this.
There are a few things to note. First, the operator > has to be defined for the kinds of elements you are sorting. In the example the elements are numbers, and > is indeed defined. Second, the sort function specifies that the boolean true orders the first one before the second rather than the other way around, so the comparison function follows that specification. Third, the last evaluated expression is taken as the boolean value to be used. Having these two assumptions beforehand allows the comparison function to be written so concisely.
So if we wanted to sort those cars by racing them, we could write it like this:
cars.sort {
winner_of_race_between($0, $1) == $0
// if the first car beats the second, it is sorted ahead
}
Or exclusive blueness:
cars.sort { //not guaranteed to be valid Swift, just consider this pseudocode
if(($0.color != Color.blue) && ($1.color == Color.blue) {
$1
} else if (($0.color == Color.blue) && ($1.color != Color.blue)) {
$0
} else { //leave them in same order
$0
}
}
extension Array {
public func mySorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element] {
var newArray: [Element] = self
if newArray.count <= 1 {
/// nothing to do
} else if newArray.count <= 32 { /// 32 ?? 64
for l in 1..<count {
for r in (0..<l).reversed() {
if try areInIncreasingOrder(newArray[r + 1], newArray[r]) {
(newArray[r + 1], newArray[r]) = (newArray[r], newArray[r + 1])
} else {
break
}
}
}
} else {
/// others sort
}
return newArray
}
}
var array: [Int] = [4, 6, 8, 1, 3]
let a1 = array.sorted {
print("\($0) \($1)")
return $0 > $1
}
print("---------")
let a2 = array.mySorted {
print("\($0) \($1)")
return $0 > $1
}
print("==========")
let a1 = array.sorted {
print("\($0) \($1)")
return $0 < $1
}
print("+++++++")
let a2 = array.mySorted {
print("\($0) \($1)")
return $0 < $1
}

In swift which loop is faster `for` or `for-in`? Why?

Which loop should I use when have to be extremely aware of the time it takes to iterate over a large array.
Short answer
Don’t micro-optimize like this – any difference there is could be far outweighed by the speed of the operation you are performing inside the loop. If you truly think this loop is a performance bottleneck, perhaps you would be better served by using something like the accelerate framework – but only if profiling shows you that effort is truly worth it.
And don’t fight the language. Use for…in unless what you want to achieve cannot be expressed with for…in. These cases are rare. The benefit of for…in is that it’s incredibly hard to get it wrong. That is much more important. Prioritize correctness over speed. Clarity is important. You might even want to skip a for loop entirely and use map or reduce.
Longer Answer
For arrays, if you try them without the fastest compiler optimization, they perform identically, because they essentially do the same thing.
Presumably your for ;; loop looks something like this:
var sum = 0
for var i = 0; i < a.count; ++i {
sum += a[i]
}
and your for…in loop something like this:
for x in a {
sum += x
}
Let’s rewrite the for…in to show what is really going on under the covers:
var g = a.generate()
while let x = g.next() {
sum += x
}
And then let’s rewrite that for what a.generate() returns, and something like what the let is doing:
var g = IndexingGenerator<[Int]>(a)
var wrapped_x = g.next()
while wrapped_x != nil {
let x = wrapped_x!
sum += x
wrapped_x = g.next()
}
Here is what the implementation for IndexingGenerator<[Int]> might look like:
struct IndexingGeneratorArrayOfInt {
private let _seq: [Int]
var _idx: Int = 0
init(_ seq: [Int]) {
_seq = seq
}
mutating func generate() -> Int? {
if _idx != _seq.endIndex {
return _seq[_idx++]
}
else {
return nil
}
}
}
Wow, that’s a lot of code, surely it performs way slower than the regular for ;; loop!
Nope. Because while that might be what it is logically doing, the compiler has a lot of latitude to optimize. For example, note that IndexingGeneratorArrayOfInt is a struct not a class. This means it has no overhead over declaring the two member variables directly. It also means the compiler might be able to inline the code in generate – there is no indirection going on here, no overloaded methods and vtables or objc_MsgSend. Just some simple pointer arithmetic and deferencing. If you strip away all the syntax for the structs and method calls, you’ll find that what the for…in code ends up being is almost exactly the same as what the for ;; loop is doing.
for…in helps avoid performance errors
If, on the other hand, for the code given at the beginning, you switch compiler optimization to the faster setting, for…in appears to blow for ;; away. In some non-scientific tests I ran using XCTestCase.measureBlock, summing a large array of random numbers, it was an order of magnitude faster.
Why? Because of the use of count:
for var i = 0; i < a.count; ++i {
// ^-- calling a.count every time...
sum += a[i]
}
Maybe the optimizer could have fixed this for you, but in this case it hasn’t. If you pull the invariant out, it goes back to being the same as for…in in terms of speed:
let count = a.count
for var i = 0; i < count; ++i {
sum += a[i]
}
“Oh, I would definitely do that every time, so it doesn’t matter”. To which I say, really? Are you sure? Bet you forget sometimes.
But you want the even better news? Doing the same summation with reduce was (in my, again not very scientific, tests) even faster than the for loops:
let sum = a.reduce(0,+)
But it is also so much more expressive and readable (IMO), and allows you to use let to declare your result. Given that this should be your primary goal anyway, the speed is an added bonus. But hopefully the performance will give you an incentive to do it regardless.
This is just for arrays, but what about other collections? Of course this depends on the implementation but there’s a good reason to believe it would be faster for other collections like dictionaries, custom user-defined collections.
My reason for this would be that the author of the collection can implement an optimized version of generate, because they know exactly how the collection is being used. Suppose subscript lookup involves some calculation (such as pointer arithmetic in the case of an array - you have to add multiple the index by the value size then add that to the base pointer). In the case of generate, you know what is being done is to sequentially walk the collection, and therefore you could optimize for this (for example, in the case of an array, hold a pointer to the next element which you increment each time next is called). Same goes for specialized member versions of reduce or map.
This might even be why reduce is performing so well on arrays – who knows (you could stick a breakpoint on the function passed in if you wanted to try and find out). But it’s just another justification for using the language construct you should probably be using regardless.
Famously stated: "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil" Donald Knuth. It seems unlikely that you are in the %3.
Focus on the bigger problem at hand. After it is working, if it needs a performance boost, then worry about for loops. But I guarantee you, in the end, bigger structural inefficiencies or poor algorithm choice will be the performance problem, not a for loop.
Worrying about for loops is oh so 1960s.
FWIW, a rudimentary playground test shows map() is about 10 times faster than for enumeration:
class SomeClass1 {
let value: UInt32 = arc4random_uniform(100)
}
class SomeClass2 {
let value: UInt32
init(value: UInt32) {
self.value = value
}
}
var someClass1s = [SomeClass1]()
for _ in 0..<1000 {
someClass1s.append(SomeClass1())
}
var someClass2s = [SomeClass2]()
let startTimeInterval1 = CFAbsoluteTimeGetCurrent()
someClass1s.map { someClass2s.append(SomeClass2(value: $0.value)) }
println("Time1: \(CFAbsoluteTimeGetCurrent() - startTimeInterval1)") // "Time1: 0.489435970783234"
var someMoreClass2s = [SomeClass2]()
let startTimeInterval2 = CFAbsoluteTimeGetCurrent()
for item in someClass1s { someMoreClass2s.append(SomeClass2(value: item.value)) }
println("Time2: \(CFAbsoluteTimeGetCurrent() - startTimeInterval2)") // "Time2 : 4.81457495689392"
The for (with a counter) is just incrementing a counter. Very fast. The for-in uses an iterator (call object to pass the next element). This is much slower. But finally you want to access the element in both cases wich will then make no difference in the end.

How exactly does the "let" keyword work in Swift?

I've read this simple explanation in the guide:
The value of a constant doesn’t need to be known at compile time, but you must assign it a value exactly once.
But I want a little more detail than this. If the constant references an object, can I still modify its properties? If it references a collection, can I add or remove elements from it? I come from a C# background; is it similar to how readonly works (apart from being able to use it in method bodies), and if it's not, how is it different?
let is a little bit like a const pointer in C. If you reference an object with a let, you can change the object's properties or call methods on it, but you cannot assign a different object to that identifier.
let also has implications for collections and non-object types. If you reference a struct with a let, you cannot change its properties or call any of its mutating func methods.
Using let/var with collections works much like mutable/immutable Foundation collections: If you assign an array to a let, you can't change its contents. If you reference a dictionary with let, you can't add/remove key/value pairs or assign a new value for a key — it's truly immutable. If you want to assign to subscripts in, append to, or otherwise mutate an array or dictionary, you must declare it with var.
(Prior to Xcode 6 beta 3, Swift arrays had a weird mix of value and reference semantics, and were partially mutable when assigned to a let -- that's gone now.)
It's best to think of let in terms of Static Single Assignment (SSA) -- every SSA variable is assigned to exactly once. In functional languages like lisp you don't (normally) use an assignment operator -- names are bound to a value exactly once. For example, the names y and z below are bound to a value exactly once (per invocation):
func pow(x: Float, n : Int) -> Float {
if n == 0 {return 1}
if n == 1 {return x}
let y = pow(x, n/2)
let z = y*y
if n & 1 == 0 {
return z
}
return z*x
}
This lends itself to more correct code since it enforces invariance and is side-effect free.
Here is how an imperative-style programmer might compute the first 6 powers of 5:
var powersOfFive = Int[]()
for n in [1, 2, 3, 4, 5, 6] {
var n2 = n*n
powersOfFive += n2*n2*n
}
Obviously n2 is is a loop invariant so we could use let instead:
var powersOfFive = Int[]()
for n in [1, 2, 3, 4, 5, 6] {
let n2 = n*n
powersOfFive += n2*n2*n
}
But a truly functional programmer would avoid all the side-effects and mutations:
let powersOfFive = [1, 2, 3, 4, 5, 6].map(
{(num: Int) -> Int in
let num2 = num*num
return num2*num2*num})
Let
Swift uses two basic techniques to store values for a programmer to access by using a name: let and var. Use let if you're never going to change the value associated with that name. Use var if you expect for that name to refer to a changing set of values.
let a = 5 // This is now a constant. "a" can never be changed.
var b = 2 // This is now a variable. Change "b" when you like.
The value that a constant refers to can never be changed, however the thing that a constant refers to can change if it is an instance of a class.
let a = 5
let b = someClass()
a = 6 // Nope.
b = someOtherClass() // Nope.
b.setCookies( newNumberOfCookies: 5 ) // Ok, sure.
Let and Collections
When you assign an array to a constant, elements can no longer be added or removed from that array. However, the value of any of that array's elements may still be changed.
let a = [1, 2, 3]
a.append(4) // This is NOT OK. You may not add a new value.
a[0] = 0 // This is OK. You can change an existing value.
A dictionary assigned to a constant can not be changed in any way.
let a = [1: "Awesome", 2: "Not Awesome"]
a[3] = "Bogus" // This is NOT OK. You may not add new key:value pairs.
a[1] = "Totally Awesome" // This is NOT OK. You may not change a value.
That is my understanding of this topic. Please correct me where needed. Excuse me if the question is already answered, I am doing this in part to help myself learn.
First of all, "The let keyword defines a constant" is confusing for beginners who are coming from C# background (like me). After reading many Stack Overflow answers, I came to the conclusion that
Actually, in swift there is no concept of constant
A constant is an expression that is resolved at compilation time. For both C# and Java, constants must be assigned during declaration:
public const double pi = 3.1416; // C#
public static final double pi = 3.1416 // Java
Apple doc ( defining constant using "let" ):
The value of a constant doesn’t need to be known at compile time, but you must assign the value exactly once.
In C# terms, you can think of "let" as "readonly" variable
Swift "let" == C# "readonly"
F# users will feel right at home with Swift's let keyword. :)
In C# terms, you can think of "let" as "readonly var", if that construct was allowed, i.e.: an identifier that can only be bound at the point of declaration.
Swift properties:
Swift Properties official documentation
In its simplest form, a stored property is a constant or variable that is stored as part of an instance of a particular class or structure. Stored properties can be either variable stored properties (introduced by the varkeyword) or constant stored properties (introduced by the let keyword).
Example:
The example below defines a structure called FixedLengthRange, which describes a range of integers whose range length cannot be changed once it is created:
struct FixedLengthRange {
var firstValue: Int
let length: Int
}
Instances of FixedLengthRange have a variable stored property called firstValue and a constant stored property called length. In the example above, length is initialized when the new range is created and cannot be changed thereafter, because it is a constant property.