Let's have two optional NSArrays. The goal is to check, if they are equal. My solution is
func isArrayEqualToArray(array1:NSArray?, array2:NSArray?) -> Bool {
let areBothEmpty:Bool = array1 == nil && array2 == nil
var areBothEqual:Bool
if !areBothEmpty && array2 != nil {
areBothEqual = array1?.isEqualToArray(array2!) ?? false
} else {
areBothEqual = false
}
let result = areBothEqual || areBothEmpty
return result
}
I feel that it is a little too verbose. It should be doable in a more concise and readable way. Does anyone have a better solution?
It is quite simple:
func isArrayEqualToArray(array1: NSArray?, array2: NSArray?) -> Bool {
return array1 == array2
}
does exactly what you want.
Why does it work? Here == is the operator that compares optionals
func ==<T : Equatable>(lhs: T?, rhs: T?) -> Bool
and that gives true if both operands are nil, or of both
operands are non-nil and the unwrapped operands are equal.
Also NSArray inherits from NSObject which conforms to Equatable,
and comparing NSObjects with == uses the isEqual: method, which is
implemented as isEqualToArray: on NSArray.
Therefore
array1 == array2
gives the same result as
array1.isEqualToArray(array2)
Yes, indeed, you don't need to overcomplicate things:
func isArrayEqualToArray(array1: NSArray?, array2: NSArray?) -> Bool {
if array1 == nil && array2 == nil {
return true;
}
if array1 != nil && array2 != nil {
return array1!.isEqualToArray(array2!);
}
return false;
}
Using a switch statement may be clearer:
func isArrayEqualToArray(a1: NSArray?, a2: NSArray?) -> Bool {
switch (a1,a2) {
case (.Some(a1),.Some(a2)):
return a1.isEqualToArray(a2)
case (.None,.None):
return true
default:
return false
}
}
I liked #The Paramagnetic Croiss answer but I'm still going to shorten it a bit because I see that I can.
```
func isArrayEqualToArray(array1: NSArray?, array2: NSArray?) -> Bool {
if array1 == array2 {
return true;
}
if array1 != nil && array2 != nil {
return array1!.isEqualToArray(array2!);
}
return false;
}
```
Related
I'm doing this,
let x = results.compactMap {
if case .person (let P) = $0 { return P }
else { return nil }
}
x is [Person] , obviously results is something like
enum ResultItem: Decodable {
case person(Person)
case cat(Cat)
case budgie(Budgie)
Anyway I'm doing this,
let x = results.compactMap {
if case .person (let P) = $0 { return P }
else { return nil }
}
inevitably someone on here can make me look like a fool and show a tighter way to do this in current Swift!
Go for it...
So results is an array of ResultItem and the idea is to extract the payloads from only the ones that are Persons?
I don't know if this is "tighter" but I would probably do it with for case let:
let arr : [ResultItem] = // ...
var persons = [Person]()
for case let .person(p) in arr { persons.append(p) }
You might reasonably object: "But I don't want persons to be a var." Yes, I know how you feel. So sometimes I say this instead (wordier, to be sure):
let persons : [Person] =
arr.reduce(into: []) {if case let .person(p) = $1 {$0.append(p)}}
They are both really ways of saying the same thing: append only if you can extract a Person. Of course you are doing that same thing, in a way, but the roundabout return nil into a compactMap is probably the objectionable part, and the second formulation is arguably a one-liner, so perhaps that's "tighter".
maybe define
func person(_ r: ResultItem)->Person? {
if case .person (let P) = r { return P }
else { return nil }
}
and next compact. at least it is easier to read for me ...
let persons = items.compactMap(person)
or even better
extension ResultItem {
static func person(_ r: ResultItem)->Person? {
if case .person (let P) = r { return P }
else { return nil }
}
}
and
let persons = items.compactMap(ResultItem.person)
(first post)
usually im able to find answers here or elsewhere but no luck this time =(
Question: in Swift, how do you filter an array that is of a protocol type by an implementing type supplied as a function parameter?
protocol Aprotocol {
var number:Int { get set }
}
class Aclass: Aprotocol {
var number = 1
}
class AnotherClass: Aprotocol {
var number = 1
}
var array:[Aprotocol] = [ Aclass(), AnotherClass(), Aclass() ]
func foo (parameter:Aprotocol) -> Int {
return array.filter({ /* p in p.self == parameter.self */ }).count
}
var bar:Aprotocol = // Aclass() or AnotherClass()
var result:Int = foo(bar) // should return 2 or 1, depending on bar type
maybe this is not the right approach at all?
thanks!
Here is what I think you want:
return array.filter { (element: Aprotocol) -> Bool in
element.dynamicType == parameter.dynamicType
}.count
But I recommend this, which does the same, but without the useless instance of Aclass() which is passed in the answer on the top. Also this way is faster:
func foo <T: Aprotocol>(type: T.Type) -> Int {
return array.filter { (element: Aprotocol) -> Bool in
element.dynamicType == type
}.count
}
var result:Int = foo(Aclass)
The dynamicType will return the Type of an instance
Very easy:
return array.filter({ parameter.number == $0.number }).count
Kametrixoms solution works (if you use "is T" instead of "== type") but in my case, since i didnt know which implementing class was going to call it, had to go with this solution:
protocol Aprotocol: AnyObject {
var number:Int { get set }
}
class func foo(parameter: AnyObject) -> Int {
return array.filter ({ (element: Aprotocol) -> Bool in
object_getClassName(element) == object_getClassName(parameter)
}).count
}
Is there any difference between the debug and release build in terms of code / dependancies?
I recently started using test flight (this uses release builds) and have had nothing but crashes. It works perfectly when I build for debug.
Anyone had this problem?
At the moment I get a exc_breakpoint when the app hits "return true"
Which seems very weird. There is nothing to go wrong when all you have to do is return "true"
import UIKit
import CoreLocation
func calculateRelevance (album: AlbumLight , currentLocation: CLLocation, currentCity: String) -> Bool {
let fromLocationLa: CLLocationDegrees = CLLocationDegrees(album.la!)
let fromLocationLo: CLLocationDegrees = CLLocationDegrees(album.lo!)
var fromLocation: CLLocation = CLLocation(latitude: fromLocationLa, longitude: fromLocationLo)
let distance = fromLocation.distanceFromLocation(currentLocation)
if album.isActive == true {
if album.hasRange == true {
if distance < Double(album.range!) {
return true
}
else {
return false
}
}
else {
if currentCity == album.city {
return true
}
else {
return false
}
}
}
else {
return false
}
}
Update :
After a lot of trial and error, I found that adding a println() to get certain values prevents my bugs. For some reason something that isn't nil is turned into nil except for when I call println() just before using the value. Makes no sense to me....
As a guess, I think the optimizer is biting you. fromLocationLa, fromLocationLo, fromLocation, and distance are only used once. This means the following optimization can be done.
…
let fromLocationLa: CLLocationDegrees = CLLocationDegrees(album.la!)
let fromLocationLo: CLLocationDegrees = CLLocationDegrees(album.lo!)
var fromLocation: CLLocation = CLLocation(latitude: fromLocationLa, longitude: fromLocationLo)
let distance = fromLocation.distanceFromLocation(currentLocation)
if album.isActive == true {
if album.hasRange == true {
if distance < Double(album.range!) {
return true
}
else {
return false
}
}
…
optimized to
if album.isActive == true {
if album.hasRange == true {
return CLLocation(latitude: CLLocationDegrees(album.la!), longitude: CLLocationDegrees(album.lo!)).distanceFromLocation(currentLocation) < Double(album.range!)
}
This could explain the odd line number in the optimized code. It also put lots of implicit unwrapping on a single line.
Given the problems you seem to be having, a bit more care might be needed when unwrapping optionals.
let fromLocationLa: CLLocationDegrees? = album.la != nil ? CLLocationDegrees(album.la!) : nil
let fromLocationLo: CLLocationDegrees? = album.la != nil ? CLLocationDegrees(album.lo!) : nil
var fromLocation: CLLocation? = fromLocationLa != nil && fromLocationLo != nil ? CLLocation(latitude: fromLocationLa!, longitude: fromLocationLo!) : nil
let distance = fromLocation?.distanceFromLocation(currentLocation)
if album.isActive == true {
if album.hasRange == true {
if distance != nil && album.range != nil && distance! < Double(album.range!) {
return true
}
else {
return false
}
}
I have a list of Swift objects that I'd like to get sorted by multiple criteria. The objects in the list are of type DateRange:
class DateRange {
var from: NSDate?
var to: NSDate?
}
The list contains many of these objects where some from or to fields are nil. I want to have this list sorted by:
First all objects that have dates
Then objects that have at least one date (either from or to)
And at the very end objects without any
The dates itself don't matter, just their existence. In Ruby I could do this (if the date is nil I set it to a very low date):
date_ranges.sort { |a, b|
[fix_nil(a.from), fix_nil(a.to)] <=> [fix_nil(b.from), fix_nil(b.to)]
}.reverse
def fix_nil(val)
val.nil? ? Date.new(0) : val
end
What's the best way to do this with Swift? Thanks in advance.
Seems like it might be a good idea to add a dateCount computed property to your DateRange type. This would be a good time for pattern matching:
extension DateRange {
// returns the number of non-nil NSDate members in 'from' and 'to'
var dateCount: Int {
switch (from, to) {
case (nil, nil): return 0
case (nil, _): return 1
case (_, nil): return 1
default: return 2
}
}
}
Then you can sort your list with a simple closure:
var ranges = [DateRange(nil, nil), DateRange(NSDate(), nil), DateRange(nil, NSDate()), DateRange(nil, nil), DateRange(NSDate(), NSDate())]
ranges.sort { $0.dateCount > $1.dateCount }
If you wanted, you could even make it Comparable with a few more lines:
extension DateRange : Comparable { }
func ==(lhs: DateRange, rhs: DateRange) -> Bool {
return lhs.dateCount == rhs.dateCount
}
func <(lhs: DateRange, rhs: DateRange) -> Bool {
return lhs.dateCount > rhs.dateCount
}
This lets you sort your list properly with an operator argument:
ranges.sort(<)
I presume that by list you mean array, so I am basing my answer on that assumption.
You can use the sort method of the array struct, which takes a closure having this signature:
(lhs: T, rhs: T) -> Bool
returning true if lhs is less than rhs, false otherwise.
I came up with this implementation:
var x: [DateRange]
// ... initialize the array
x.sort { (lhs: DateRange, rhs: DateRange) -> Bool in
if lhs.from != nil && lhs.to != nil {
return true
}
if lhs.from == nil && lhs.to == nil {
return false
}
return rhs.from == nil && rhs.to == nil
}
if lhs has both properties not nil, then it comes first, regardless of rhs
if lhs has both properties nil, then if comes after, regardless of rhs
else lhs has one nil, the other not nil, and in that case it comes first only if rhs has both properties nil
If you plan to reuse the sort in several places, it's better to move the code out of the sort method - the best place is probably an overload of the < operator:
func < (lhs: DateRange, rhs: DateRange) -> Bool {
if lhs.from != nil && lhs.to != nil {
return true
}
if lhs.from == nil && lhs.to == nil {
return false
}
return rhs.from == nil && rhs.to == nil
}
and in that case it can be used as follows:
x.sort(<)
If you don't like the operator overload, you can of course give that function any other name.
Note that sorting is done in place.
Here is how I would approach this. To keep things simple, add a scoring function for the date range. In your scenario, you have 3 possibilities:
nil & nil : 0 points
nil & date : 1 point
date & date : 2 points
import Foundation
class DateRange {
var from: NSDate?
var to: NSDate?
init(from: NSDate?, to: NSDate?)
{
self.from = from
self.to = to
}
func scoreDateRange() -> Int
{
var score = 0
if from != nil
{
score++
}
if to != nil
{
score++
}
return score
}
}
func sortDateRange( d1 : DateRange, d2 : DateRange)-> Bool
{
return d1.scoreDateRange() > d2.scoreDateRange()
}
var date_ranges = [DateRange]()
date_ranges.append(DateRange(from:nil, to:nil))
date_ranges.append(DateRange(from:nil, to:nil))
date_ranges.append(DateRange(from:NSDate(), to:NSDate()))
date_ranges.append(DateRange(from:nil, to:NSDate()))
date_ranges.append(DateRange(from:NSDate(), to:nil))
date_ranges.append(DateRange(from:NSDate(), to:NSDate()))
date_ranges.sort(sortDateRange)
I am building a very simple structure in Swift that contains an array of optional values. This struct must conform to the Equatable protocol. This is the code:
struct MyTable: Equatable {
var values: [Int?] = Array(count: 64, repeatedValue: nil)
}
func == (lhs: MyTable, rhs: MyTable) -> Bool {
return lhs.values == rhs.values
}
Quite simple. I see no mistakes, but the compiler gives error: "'[Int?]' is not convertible to 'MyTable'". Am I doing something stupid? or is this a compiler's bug? Thanks!
(Using Xcode6-Beta5)
The reason why it does not work is there is no == operator defined for arrays with optional elements, only for non-optional elements:
/// Returns true if these arrays contain the same elements.
func ==<T : Equatable>(lhs: [T], rhs: [T]) -> Bool
You can provide your own:
func ==<T : Equatable>(lhs: [T?], rhs: [T?]) -> Bool {
if lhs.count != rhs.count {
return false
}
for index in 0..<lhs.count {
if lhs[index] != rhs[index] {
return false
}
}
return true
}
Another useful option is to use the elementsEqual:isEquivalent: method available on SequenceType. This could allow you to avoid implementing Equatable, but is best used rarely as it is more verbose.
Usage:
let a: [Int?] = []
let b: [Int?] = []
if a.elementsEqual(b, isEquivalent: { $0 == $1 }) {
print("foo") // Works
}