I am a swift newbie. I have created a new method in the Set data structure for my code. This method inserts into set only if the value is not equal to a particular number.
extension Set {
mutating func addExcludingCurrent(value: Int, currIndex: Int) {
if value == currIndex {
return
} else {
self.insert(value) // Error
}
}
}
Cannot invoke 'insert' with an argument list of type '(Int)' . How do I resolve it. Or else is there a better way to do this task ??
A Set is a struct whose AssociatedType is defined as Element. So you can extend it by inserting Elements, and not integers:
extension Set {
mutating func addExcludingCurrent(value: Element, currIndex: Element) {
if value == currIndex {
return
} else {
self.insert(value)
}
}
}
var a = Set<Int>();
a.insert(3)
a.addExcludingCurrent(5,currIndex: 5)
Try this:
extension Set {
mutating func addExcludingCurrent(value: Element, currIndex: SetIndex<Element>) {
if value == currIndex {
return
} else {
self.insert(value) // No more mistakes
}
}
}
In Swift Index is defined as follows:
public typealias Index = SetIndex<Element>
So you could write extension like this:
extension Set {
mutating func addExcludingCurrent(value: Element, currIndex: Index) {
if value == currIndex {
return
} else {
self.insert(value) // No more mistakes
}
}
}
Related
I have this function that picks a random value from a list:
func pickAttraction(attractionType: Array<Attraction>) -> Attraction {
var randAttr = attractionType.randomElement()
if favoritesNames.contains(randAttr!.attractionName) {
return pickAttraction(attractionType: attractionType)
} else {
return randAttr!
}
}
If the random value is in the array favoritesName, I want it to go back to the top of the function and pick a new value.
How can I make this more efficient?
In addition, is it possible for the program to crash while force-unwrapping randAttr (on line 3) if attractionType always has at least 8 values?
The question is not worth asking! It'll never get you there.
func pickAttraction(from attractions: [Attraction]) -> Attraction? {
attractions.filter { !favoritesNames.contains($0.name) }
.randomElement()
}
Here is a sample in playground
import UIKit
class RandomAttraction:Hashable{
static func == (lhs: RandomAttraction, rhs: RandomAttraction) -> Bool {
return lhs.attractionName == rhs.attractionName
}
func hash(into hasher: inout Hasher) {
hasher.combine(attractionName)
}
init(attractionName:String) {
self.attractionName = attractionName
}
var attractionName:String
}
var favoritesNames = [RandomAttraction.init(attractionName: "a"),RandomAttraction.init(attractionName: "b")]
var otherNames = [RandomAttraction.init(attractionName: "c"),RandomAttraction.init(attractionName: "d")]
func pickAttraction(attractionType: Array<RandomAttraction>) -> RandomAttraction? {
//Filters the content of the favoritesNames from attractionType
let filteredArray = Array(Set(attractionType).subtracting(favoritesNames))
//If count is zero return nil
if filteredArray.count == 0{
return nil
}
let randAttr = filteredArray.randomElement()
return randAttr
}
//MARK: Usage
if let pickedAttraction = pickAttraction(attractionType: otherNames){
print(pickedAttraction.attractionName)
}else{
print("No attractions")
}
I wanted to be notified when an element added/removed from an array. If we are not talking about arrays, for example to be notified when a string is changed, there is a good solution in swift:
private var privateWord: String?
var word: String? {
get {
return privateWord
}
set {
if newValue != "" {
notifyThatWordIsChanged()
} else {
notifyThatWordIsEmpty()
}
privateWord = newValue
}
}
Can we achive a similar result, when I add/remove an element to an array?
You can create proxy like class/struct that will have same interface as array, will store standard array under the scenes and will act on behalf of store array. Here is small example:
struct ArrayProxy<T> {
var array: [T] = []
mutating func append(newElement: T) {
self.array.append(newElement)
print("Element added")
}
mutating func removeAtIndex(index: Int) {
print("Removed object \(self.array[index]) at index \(index)")
self.array.removeAtIndex(index)
}
subscript(index: Int) -> T {
set {
print("Set object from \(self.array[index]) to \(newValue) at index \(index)")
self.array[index] = newValue
}
get {
return self.array[index]
}
}
}
var a = ArrayProxy<Int>()
a.append(1)
I have a container class that has an underlying dictionary. I have implemented subscripts for this class to access member of the underlying dictionary. Now, I am trying to create a sequence on this class so that I could iterate over all the elements of the underlying dictionary by using 'for-in' loop on the class instance itself. I have been looking to find some examples for Sequences for Swift Dictionary but could not find anything that explains the stuff well. I have seen some custom sequence examples for Swift Array but none for the Swift Dictionary. I would really appreciate if anyone could explain how I can achieve that. Following is the code for the class (no sequence code yet as I am not sure where to begin)
import Foundation
class STCQuestionList : GeneratorType, SequenceType {
private var questionDict: [String : STCQuestion] = [ : ];
subscript(key : String?) -> STCQuestion? {
get {
if (key != nil) {
return self.questionDict[key!];
}
return nil;
}
set(newValue) {
if (key != nil) {
self.questionDict[key!] = newValue;
}
}
}
func generate() -> GeneratorType {
}
func next() -> (String, STCQuestion)? {
if (self.questionDict.isEmpty) {
return .None
}
}
}
If I'm understanding correctly, how about just forwarding on the generate?
func generate() -> DictionaryGenerator<String, STCQuestion> {
return questionDict.generate()
}
(You don't need to implement GeneratorType, just SequenceType should do. It's generate() itself that returns a GeneratorType, and that's what has to implement next(), which the existing generate() implementation in Dictionary already does for you.)
Full worked example based on your code:
// Playground - noun: a place where people can play
import Foundation
class STCQuestion {
let foo: String
init(_ foo: String) {
self.foo = foo
}
}
class STCQuestionList : SequenceType {
private var questionDict: [String : STCQuestion] = [ : ];
subscript(key : String?) -> STCQuestion? {
get {
if key != nil {
return self.questionDict[key!];
}
return nil;
}
set(newValue) {
if key != nil {
self.questionDict[key!] = newValue;
}
}
}
func generate() -> DictionaryGenerator<String, STCQuestion> {
return questionDict.generate()
}
}
var list = STCQuestionList()
list["test"] = STCQuestion("blah")
list["another"] = STCQuestion("wibble")
list["third"] = STCQuestion("doodah")
for (key, value) in list {
println("Key: \(key) Foo: \(value.foo)")
}
// Output:
// Key: test Foo: blah
// Key: another Foo: wibble
// Key: third Foo: doodah
(Note: I re-thought this -- original answer via the edited page...)
Swift has a generic GeneratorOf type that you can use to create a generator. You just provide a closure that returns the next value in the initializer:
class STCQuestionList : SequenceType {
private var questionDict: [String : STCQuestion] = [ : ];
subscript(key : String?) -> STCQuestion? {
get {
if (key != nil) {
return self.questionDict[key!];
}
return nil;
}
set(newValue) {
if (key != nil) {
self.questionDict[key!] = newValue;
}
}
}
/// Creates a generator for each (key, value)
func generate() -> GeneratorOf<(String, STCQuestion)> {
var index = 0
return GeneratorOf<(String, STCQuestion)> {
if index < self.questionDict.keys.array.count {
let key = self.questionDict.keys.array[index++]
return (key, self.questionDict[key]!)
} else {
return nil
}
}
}
}
If you don't care about the order, can't you just call the same methods of dictionary or make your class a subclass of a dictionary? For example:
func generate() -> GeneratorType {
return self.questionDict.generate()
}
func next() -> (String, STCQuestion)? {
return self.questionDict.next()
}
I'm trying to add an extension method in Array like so:
extension Array {
func contains(obj: T) -> Bool {
let filtered = self.filter {$0 == obj}
return filtered.count > 0
}
}
But self.filter {$0 == obj} don't work. Compiler error:
could not find an overload for '==' that accepts the supplied arguments
you don't actually need to write an extension, you can use the global func contains from the Swift library:
contains([1,2,3], 1)
Swift 1.x
As I mentioned in the comments, there is a contains function. But to answer the question of how to write an extension and what the compiler error means:
The elements in the array can't necessarily be compared with ==. You need to make sure the parameter is Equatable and you need to make sure the array element is of the same type.
extension Array {
func contains<T : Equatable>(obj: T) -> Bool {
let filtered = self.filter {$0 as? T == obj}
return filtered.count > 0
}
}
Swift 2/Xcode 7 (Beta)
Swift 2 includes SequenceType.contains, which is exactly what you were trying to create.
This is made possible by a Swift syntax that allows restricting methods to certain (e.g. Equatable) type arguments. It looks like this:
extension SequenceType where Generator.Element: Equatable {
func contains(element: Self.Generator.Element) -> Bool {
...
}
}
I found that the built-in contains doesn't work with reference types. I needed this and solved it with the code below. I'm pasting it here because somebody else might be confused about contains() like I was.
extension Array {
func containsReference(obj: AnyObject) -> Bool {
for ownedItem in self {
if let ownedObject: AnyObject = ownedItem as? AnyObject {
if (ownedObject === obj) {
return true
}
}
}
return false
}
}
This works with Swift 2.1 for reference types pretty good.
extension SequenceType where Generator.Element: AnyObject {
func contains(obj: Self.Generator.Element?) -> Bool {
if obj != nil {
for item in self {
if item === obj {
return true
}
}
}
return false
}
}
For value types you can add this:
extension SequenceType where Generator.Element: Equatable {
func contains(val: Self.Generator.Element?) -> Bool {
if val != nil {
for item in self {
if item == val {
return true
}
}
}
return false
}
}
Not perfect, but this version built on nschum's answer supports optional arguments (though not arrays with optional types) as well:
extension Array {
private func typeIsOptional() -> Bool {
return reflect(self[0]).disposition == .Optional
}
func contains<U : Equatable>(obj: U) -> Bool {
if isEmpty {
return false
}
if (typeIsOptional()) {
NSException(name:"Not supported", reason: "Optional Array types not supported", userInfo: nil).raise()
}
// cast type of array to type of argument to make it equatable
for item in self.map({ $0 as? U }) {
if item == obj {
return true
}
}
return false
}
// without this version, contains("foo" as String?) won't compile
func contains<U : Equatable>(obj: U?) -> Bool {
if isEmpty {
return false
}
if (typeIsOptional()) {
NSException(name:"Not supported", reason: "Optional Array types not supported", userInfo: nil).raise()
}
return obj != nil && contains(obj!)
}
}
If you have an array of optionals, you can get a copy of it with non-optionals (nil arguments removed) with this global function thanks to jtbandes:
func unwrapOptionals<T>(a: [T?]) -> [T] {
return a.filter { $0 != nil }.map { $0! }
}
Usage:
1> func unwrapOptionals<T>(a: [T?]) -> [T] {
2. return a.filter { $0 != nil }.map { $0! }
3. }
4>
5> let foo = ["foo" as String?]
foo: [String?] = 1 value {
[0] = "foo"
}
6> let bar = unwrapOptionals(foo)
bar: [String] = 1 value {
[0] = "foo"
}
For good measure, add one that just returns the array if its type is not optional. This way you avoid runtime errors if you call unwrapOptionals() on a non-optional array:
func unwrapOptionals<T>(a: [T]) -> [T] {
return a
}
Note you might think you could just call unwrapOptionals inside func contains<U : Equatable>(obj: U?). However, that doesn't work, because the Element type in the Array extension is just a type--it doesn't "know" it's an optional type. So if you call unwrapOptionals, the second version will be invoked, and you'll just get the array full of optionals back.
I have been trying to implement an iOS app in Swift from Big Nerd Ranch's most recent iOS book.
In the guide, there is the following method, where privateItems is an NSMutableArray
- (void)removeItem:(BNRItem *)item
{
[self.privateItems removeObjectIdenticalTo:item]
}
So in my Swift implementation, which has privateItems as a Swift Array, I would like to be able to do this:
func removeItem(item: Item) {
_privateItems.removeObjectIndenticalTo(item)
}
However, the Swift Array does not support that method. It has the removeAtIndex method. I know that I could simply achieve the same effect with the following code:
func removeItem(item: Item) {
for (index, element) in enumerate(_privateItems) {
if element === item {
_privateItems.removeAtIndex(index)
}
}
}
However, I would like to create a generic extension for the Swift Array so that I don't have to do this every time. Not a huge deal, but I think it would at least be good for the sake of learning.
I have looked into a couple of other SO posts (How can I extend typed Arrays in Swift? and How can we create a generic Array Extension that sums Number types in Swift?), but have not been able to accomplish what I want. Here is my attempt:
protocol Identifiable {
func ===(lhs: Self, rhs: Self) -> Self
}
extension Array {
func removeObjectIndenticalTo<T: Identifiable>(object: T) {
for (index, element) in enumerate(self) {
if object === element {
removeAtIndex(index)
}
}
}
}
I think I am pretty close. This gives the error "Could not find an overload for '===' that accepts the supplied arguments". What should I change to accomplish that?
Also for reference, there was another SO post that extended the Swift Array with some of the NSMutableArray methods in the following fashion:
extension Array {
func contains(#object:AnyObject) -> Bool {
return self.bridgeToObjectiveC().containsObject(object)
}
func indexOf(#object:AnyObject) -> Int {
return self.bridgeToObjectiveC().indexOfObject(object)
}
}
protocol Identifiable {
#infix func ===(a: Self, b: Self) -> Bool
}
struct Item : Identifiable {
var n : Int
}
#infix func ===(a: Item, b: Item) -> Bool {
return a.n == b.n
}
var _privateItems = [Item(n:1),Item(n:2),Item(n:2),Item(n:1),Item(n:2)]
func removeItem(item: Item) {
var tbr : Int[] = []
for (index, element) in enumerate(_privateItems) {
if element === item {
tbr += index
}
}
for i in reverse(sort(tbr)) {
_privateItems.removeAtIndex(i)
}
}
removeItem(Item(n:2))
println(_privateItems.count)
removeItem(Item(n:1))
println(_privateItems.count)