Create a typed array w/ N items in it using Swift - ios

I have a method that I'm using to create an array w/ n of a specific type of object in it:
func many(count: Int) -> [Cube] {
var cubes: [Cube] = []
for i in 0...count {
let cube = CubeFactory.single()
cubes.append(cube)
}
return cubes
}
This works but I'm sure there's a more swift like way to do this. Any one know how this can be refactored?

Does CubeFactory.single() actually do anything other than return the same instance every time?
If it does active stuff, return map(0..<count) { _ in CubeFactory.single() } (or numerous variants) will give you an array of them.
(the _ in really oughtn’t to be necessary, but Swift’s type inference gets a bit flummoxed without it and you’ll get an error about half-open intervals – the other possibility for the overloaded ..< operator – not conforming to SequenceType)
If it’s just inert and every cube is identical, return Array(count: count, repeatedValue: Cube.single()) is all you need.

In Swift 2, this is
let array = (0..<30).map { _ in CubeFactory.single() }

Not sure if it's exactly what you're looking for, but you can use:
let array = map(0..<30) { _ in CubeFactory.single() }

Swift 4.2
struct Cube {
let id:Int
}
...
let array = (0..<24).compactMap {
return Cube(id: $0)
}

Related

Dictionary not appending values

I think I'm introducing some logic error and I might be missing something here.
Please consider the following code:
// Model
class MyModel: NSObject {
let month: Int
let destiny: String
init(month: Int, destiny: String) {
self.month = month
self.destiny = destiny
}
}
var datasource: [MyModel] = []
var dict: [Int : [MyModel]] = [:]
func fillDatasource() {
for _ in 0...20 {
let month = Int.random(in: 1...12)
let destiny = "Any"
let model = MyModel(month: month, destiny: destiny)
datasource.append(model)
}
}
func fillDict() {
datasource.forEach {
let month = $0.month
dict[month]?.append($0)
}
print(dict) // always empty
}
fillDatasource()
fillDict()
Inside my fillDict function the array is always nil.
I think this is because the key doesn't exist , so the value cannot be appended to that specific key.
My question is: if the key doesn't exist, calling the append function would insert the key as well?
Am I missing something here?
Thanks.
Your assumption is incorrect and there is no reason to think that this would insert a new array.
It might seem intuitive for this case but it may be very wrong for some cases. How about something like this:
garages[myName]?.parkCar(myCar)
Should this construct a new garage for my car? I think not. But even if so; what if default constructor is unavailable and this is actually defined as a protocol:
protocol Garage {
func parkCar(_ car: Car)
}
var garages[String: Garage]
there is no way for Swift to fill in this object automatically.
Technically there would be a possible solution for this work that Swift would automatically construct an object for you in dictionary if this object had a default constructor and possibly the object type is a struct or a final class... But this would most likely only introduce more confusion than it would solve.
The most straight forward solution to your example is what #Sh_Khan wrote (but later deleted) which is:
if dict[month] == nil {
dict[month] = [$0]
}
else {
dict[month]?.append($0)
}
Probably some more feasible approach would be
dict[month] = (dict[month] ?? []) + [$0]
but as described in a comment there is already a method that does exactly that for you:
dict[month, default: []].append($0)
I hope we can agree that this is a more general approach and it fixes all cases. For instance
garages[myName, default: PublicGarage(parkingSpot: myName)].parkCar(myCar)
You can update your fillDict method to the following:
func fillDict() {
datasource.forEach {
let month = $0.month
if dict.keys.contains(month) {
dict[month]?.append($0)
} else {
dict[month] = [$0]
}
}
print(dict)
}
Explanation:
We need to check if the month key already exits in the dictionary, than append in it's array else we are assigning a new array against a month in the dictionary
Dic is empty because dic[month] is nil, the value has never been altered.
To group array by a property of the array elem, I'd use the following:
dic = Dictionary(grouping: datasource) { (model) -> Int in
return model.month
}

How to declare a variable to host multiple values [duplicate]

In The Swift Programming Language, it says:
Functions can also take a variable number of arguments, collecting them into an array.
func sumOf(numbers: Int...) -> Int {
...
}
When I call such a function with a comma-separated list of numbers (`sumOf(1, 2, 3, 4), they are made available as an array inside the function.
Question: what if I already have an array of numbers that I want to pass to this function?
let numbers = [1, 2, 3, 4]
sumOf(numbers)
This fails with a compiler error, “Could not find an overload for '__conversion' that accepts the supplied arguments”. Is there a way to turn an existing array into a list of elements that I can pass to a variadic function?
Splatting is not in the language yet, as confirmed by the devs. Workaround for now is to use an overload or wait if you cannot add overloads.
Here's a work around that I found. I know it's not exactly what you want, but it seems to be working.
Step 1: Declare the function you'd like with an array instead of variadic arguments:
func sumOf(numbers: [Int]) -> Int {
var total = 0
for i in numbers {
total += i
}
return total
}
Step 2: Call this from within your variadic function:
func sumOf(numbers: Int...) -> Int {
return sumOf(numbers)
}
Step 3: Call Either Way:
var variadicSum = sumOf(1, 2, 3, 4, 5)
var arraySum = sumOf([1, 2, 3, 4, 5])
It seems strange, but it is working in my tests. Let me know if this causes unforeseen problems for anyone. Swift seems to be able to separate the difference between the two calls with the same function name.
Also, with this method if Apple updates the language as #manojid's answer suggests, you'll only need to update these functions. Otherwise, you'll have to go through and do a lot of renaming.
You can cast the function:
typealias Function = [Int] -> Int
let sumOfArray = unsafeBitCast(sumOf, Function.self)
sumOfArray([1, 2, 3])
You can use a helper function as such:
func sumOf (numbers : [Int]) -> Int { return numbers.reduce(0, combine: +) }
func sumOf (numbers : Int...) -> Int { return sumOf (numbers) }
I did this (Wrapper + Identity Mapping):
func addBarButtonItems(types: REWEBarButtonItemType...) {
addBarButtonItems(types: types.map { $0 })
}
func addBarButtonItems(types: [REWEBarButtonItemType]) {
// actual implementation
}
I know this response does not answer your exact question, but I feel its worth noting. I too was starting to play with Swift and immediately ran into a similar question. Manojlds answer is better for your question, I agree, but again, another workaround I came up with. I do happen to like Logan's better too.
In my case I just wanted to pass an array:
func sumOf(numbers: Array<Int>) -> Int {
var sum = 0
for number in numbers {
sum += number
}
return sum
}
var someNums = [8,7,2,9,12]
sumOf(someNums)
sumOf([10, 15, 20])
Just wanted to share, in case anyone else was thinking like me. Most of the time I would prefer pass the array like this, but I don't think the "Swiftly" yet. :)
Swift 5
This is an approach with #dynamicCallable feature that allows to avoid overloading or unsafeBitCast but you should make a specific struct to call:
#dynamicCallable
struct SumOf {
func dynamicallyCall(withArguments args: [Int]) -> Int {
return args.reduce(0, +)
}
}
let sum = SumOf()
// Use a dynamic method call.
sum(1, 2, 3) // 6
// Call the underlying method directly.
sum.dynamicallyCall(withArguments: [1, 2, 3]) // 6

Check duplicates properties on Swift array

I have a custom class called Place with 3 properties:
Name (String)
Category (String)
GeoPoint (CLLocationCoordinate2D)
I have an array of type [Place] of 100 objects and I want to check if there are duplicates on the GeoPoint property (just on this one).
How can I check duplicates of a specific property in an array of custom objects?
Thanks!
Although the accepted answer is good, I'd like to chip in.
There are two more ways to achieve what you want, and they both benefit from functionalities provided by SDK.
1 - Use Sets as Tj3n mentioned in a comment.
To achieve this you would need to make your Place conform to Hashable protocol.
class Place : Hashable {
var name = ""
var category = ""
var geoPoint: CLLocationCoordinate2D = CLLocationCoordinate2D()
var hashValue: Int {
get {
return geoPoint.longitude.hashValue &+ geoPoint.latitude.hashValue
}
}
}
func ==(lhs: Place, rhs: Place) -> Bool {
return lhs.geoPoint.latitude == rhs.geoPoint.latitude && lhs.geoPoint.longitude == rhs.geoPoint.longitude
}
The &+ operator in hashValue means "add, and don't crash at overflow". Using it is as straightforward as it can - let set = Set(yourArrayOfPlaces) - the set will contain only unique, in regard to geoPoint, places.
2 - Use KVC. While this is more of an Objective-C world, I find it a useful tool. To achieve this, you'd need to make Place inherit from NSObject. Then getting an array of unique places can be reduced to this one line :
let uniquePlaces = (yourPlacesArray as NSArray).value(forKeyPath: "#distinctUnionOfObjects.geoPoint")
You can do something like this:
var dict : [String : Int] = [:]
for place in arr {
if dict[place.GeoPoint] != nil { // Not in dictionary
if dict[place.GeoPoint] >= 1 { // If already there
return true // Duplicate
} else {
dict[place.GeoPoint]! += 1 // Increment instance
}
} else {
dict[place.GeoPoint] = 0 // Put in dictionary
}
}
return false // No duplicates
Where you loop through a [Place] array and check to see how many have the same GeoPoint. Then check to see if there's one there more than once.

How to covert NSMutableOrderedSet to generic array?

I have this for loop, p is a NSManagedObject, fathers is a to-many relationship, so I need to cast NSMutableOrderedSet to [Family] but it does not work, why?
for f in p.fathers as [Family] {
}
You can obtain an array representation of the set via the array property - then you can downcast it to the proper type and assign to a variable:
let families = p.fathers.array as [Family]
but of course you can also use it directly in the loop:
for f in p.fathers.array as [Family] {
....
}
Update
A forced downcast is now required using the ! operator, so the code above should be:
let families = p.fathers.array as! [Family]
The simple solution by Antonio should be used in this case. I'd just like to discuss this a bit more. If we try to enumerate an instance of 'NSMutableOrderedSet' in a 'for' loop, the compiler will complain:
error: type 'NSMutableOrderedSet' does not conform to protocol
'SequenceType'
When we write
for element in sequence {
// do something with element
}
the compiler rewrites it into something like this:
var generator = sequence.generate()
while let element = generator.next() {
// do something with element
}
'NS(Mutable)OrderedSet' doesn't have 'generate()' method i.e. it doesn't conform to the 'SequenceType' protocol. We can change that. First we need a generator:
public struct OrderedSetGenerator : GeneratorType {
private let orderedSet: NSMutableOrderedSet
public init(orderedSet: NSOrderedSet) {
self.orderedSet = NSMutableOrderedSet(orderedSet: orderedSet)
}
mutating public func next() -> AnyObject? {
if orderedSet.count > 0 {
let first: AnyObject = orderedSet.objectAtIndex(0)
orderedSet.removeObjectAtIndex(0)
return first
} else {
return nil
}
}
}
Now we can use that generator to make 'NSOrderedSet' conform to 'SequenceType':
extension NSOrderedSet : SequenceType {
public func generate() -> OrderedSetGenerator {
return OrderedSetGenerator(orderedSet: self)
}
}
'NS(Mutable)OrderedSet' can now be used in a 'for' loop:
let sequence = NSMutableOrderedSet(array: [2, 4, 6])
for element in sequence {
println(element) // 2 4 6
}
We could further implement 'CollectionType' and 'MutableCollectionType' (the latter for 'NSMutableOrderedSet' only) to make 'NS(Mutable)OrderedSet' behave like Swift's standard library collections.
Not sure if the above code follows the best practises as I'm still trying to wrap my head around details of all these protocols.

Swift: How to write a variant of enumerate that takes an optional sequence type

I tried to add an override for enumerate that handles the case of an optional sequence (without crashing). The idea was that if the sequence is valid aka .Some, it would enumerate that sequence, otherwise enumerate an empty sequence:
func enumerate2<Seq : SequenceType>(base: Seq?) -> EnumerateSequence<Seq> {
// if optional sequence is specified
if let b = base { return enumerate(b) }
// enumerate empty sequence
let a = Array<Seq.Generator.Element>()
return Swift.enumerate(a)
}
func enumerate3<Seq : SequenceType, T where T == Seq.Generator.Element>(base: Seq?) -> EnumerateSequence<Seq> {
// if optional sequence is specified
if let b = base { return enumerate(b) }
// enumerate empty sequence
let a = Array<T>()
return Swift.enumerate(a)
}
I am seeing the error:
'Array<Seq.Generator.Element>' does not conform to protocol 'GeneratorType'
on the last return lines: return Swift.enumerate(a)
This confuses me as EnumerateSequence<Seq> does not appear to conform to GeneratorType. This seems like a simple enough exercise, what could I be missing?
Note that the above code is split apart for illustration, and the suffixes 2 and 3 are to keep remove ambiguity.
Edit:
One work around is to return an Optional sequence instead of an empty sequence.
func enumerate<Seq : SequenceType>(base: Seq?) -> EnumerateSequence<Seq>? {
return base != nil ? enumerate(base) : nil
}
The problem then shifts to safely enumerating optionals:
public func each<S:SequenceType, T where T == S.Generator.Element>
(seq: S, with fn:(T)->()) {
for s in seq { fn(s) }
}
public func each<S:SequenceType, T where T == S.Generator.Element>
(seq: S?, with fn:(T)->()) {
if let some = seq {
for s in some { fn(s) }
}
}
let es = enumerate(["a", "b", "c", "d"])
each(es) { p in
println("\(p.0), \(p.1)")
}
let b:[Int]? = nil
let nes = enumerate(b)
each(nes) { p in
println("\(p.0), \(p.1)")
}
println("done")
which produces:
0, a
1, b
2, c
3, d
done
Perhaps though, the explicit use of if let... is better just for being more obvious, but I am still curious about the initial compilation error.
This isn't working because your function declaration says you're returning an EnumerateSequence<Seq>, but that last line would return a EnumerateSequence <Array<Seq.Generator.Element>>—those aren't the same, so the compiler won't allow it.
You need to be able to create an empty instance of Seq type, but the SequenceType protocol doesn't specify an initializer - you need to go down the chain to ExtensibleCollectionType to find a protocol with an initializer, so change your generic constraint to that. Then you can do this:
func enumerate3<Seq : ExtensibleCollectionType>(base: Seq?) -> EnumerateSequence<Seq> {
// if optional sequence is specified
if let b = base { return enumerate(b) }
// enumerate empty sequence
let a = Seq()
return enumerate(a)
}
Note: if you look through the Swift headers, it won't show you that Array conforms to ExtensibleCollectionProtocol, but it actually does through a "hidden" ArrayType protocol.
There was a good solution to this posted on dev forurms.
The solution requires altering the return type and and using SequenceOf(EmptyCollection>(...)), but that does not appear to have any negative impact since the compiler will already differentiate based on the optional argument type. The code is:
func enumerate<Seq: SequenceType>(base: Seq?) -> SequenceOf<(Int, Seq.Generator.Element)> {
if let base = base {
return SequenceOf(Swift.enumerate(base))
} else {
return SequenceOf(EmptyCollection<(Int, Seq.Generator.Element)>())
}
}

Resources