I tried to create a custom iterator which returns wrapper abcContainer over raw data class abc
// raw data class
class abc {
var name : String = "";
init( _ value : String) {
name = value;
}
}
// with container, only "name" is to be visible
class abcContainer {
private var _abc : abc;
init( _ obj : abc) {
_abc = obj;
}
// + extra methods here
func getName() -> String {
return _abc.name
}
}
The point would be that the dictionary would return instances of abcContainer instead of just the plain raw abc class.
I wanted to use the sequence protocol to make the conversion automatic, but I was not able to transform the [String:abc] into [String:abcContainer] automatically like this:
// the iterator is implemented just iterating the inner basic dict
// but wrapping the result value as abcContainer
class abcIterator : Sequence, IteratorProtocol {
private var __source : [String:abc]?;
var index = 0
var myIterator : DictionaryIterator<String, abc>;
init(_ ctxArray: [String:abc]) {
self.__source = ctxArray
index = 0;
myIterator = (__source?.makeIterator())!
}
func next() -> abcContainer? {
let nextItem = myIterator.next();
if(nextItem != nil) {
return abcContainer((nextItem?.value)!);
}
return nil;
}
}
// this was supposed to be the wrapper over the collection
class abcCollection : Sequence {
private var __source : [String:abc]?;
init(_ list: [String:abc]) {
self.__source = list
}
func makeIterator() -> abcIterator {
return abcIterator(self.__source!);
}
}
I'm probably missing something very basic here. When I try to use the collection like this:
var dict : [String:abc] = [String:abc]();
dict["abba"] = abc("John Smith");
for (key,value) in abcCollection(dict) {
print(key, value.getName());
}
I get error: Expression type "abcCollection" is ambiguous without more context
Does anyone have idea how to make it work? What is missing? I have a feeling that this answer has the information I need...
Swift 2 to 3 Migration for Swift Sequence Protocol
The problem in your original code is that abcCollection(dict)
returned a sequence of abcContainer objects, and those cannot
be assigned to a (key, value) tuple.
You can achieve your goal with
class abcCollection : Sequence {
private var __source : [String:abc]
init(_ list: [String:abc]) {
self.__source = list
}
public func makeIterator() -> AnyIterator<(AnyObject,abcContainer)> {
let mapped = self.__source.lazy.map {
($0.key as AnyObject, abcContainer($0.value))
}
return AnyIterator(mapped.makeIterator())
}
}
Making __source non-optional makes all the (optional) unwrappings
redundant, and lazy.map { ... } returns a lazily evaluated
sequence of key/value pairs which is then type-erased.
Ok, perhaps the answer was abcIterator was not necessary, you could have defined the iterator directly just like done in the linked answer like this:
class abcCollection : Sequence {
private var __source : [String:abc]?;
init(_ list: [String:abc]) {
self.__source = list
}
public func makeIterator() -> AnyIterator<(AnyObject,abcContainer)> {
var it = self.__source?.makeIterator();
return AnyIterator {
let n = it?.next();
if n == nil { return nil }
return (n?.key as AnyObject, abcContainer((n?.value)!))
}
}
}
After that, the custom collection returned wrapped objects correctly.
Related
New to Swift. Have some understanding of classes. I have a getSomething() function that is supposed to return an array of key values (not the value itself, but only their keys) appended from a dict. However, my function is only returning nil
public class MyClass {
private var somethingA : String
private var somethingB : String
private var somethingC : Int
// A dictionary
// For example, if I add a fruit, color and price
// ["Apple":["red":5]]
private var complexes:[String:[String:Int]] = [String:[String:Int]]();
init() {
self.somethingA = "";
self.somethingB = "";
self.somethingC = 0;
self.complexes = [somethingA:[somethingB:somethingC]];
}
// Adds a string to the dict complexes
// For example, ["Apple] = ["", 0]
public func addSomething(somethingAA : String) {
self.somethingA = somethingAA;
self.complexes[somethingAA] = [self.somethingB : self.somethingC];
}
// Adds a whole entry to the dict complexes
// For example, ["Apple"] = ["red": 5]
public func addComplex(somethingAA: String, complex:(somethingBB: String, somethingCC: Int)) {
self.somethingA = somethingAA
self.somethingB = complex.somethingBB
self.somethingC = complex.somethingCC
complexes[somethingAA] = [complex.somethingBB: complex.somethingCC]
}
// My Problem: only returns nil
public func getSometing() -> [String]?{
var myArray:[String]? = nil;
let keys = Array(complexes.keys);
for key in keys { myArray?.append(key); } // This may be the line of code that isn't properly working
return myArray;
}
}
My testing file:
var sampleObject:MyClass;
sampleObject = MyClass();
sampleObject.addSomething(somethingAA: "Apple");
sampleObject.addSomething(somethingAA: "Orange");
print(sampleObject.getSomething());
My getSomething() function only prints nil meaning that it's not appending the proper keys into myArray
Not exactly sure why. Haven't really worked or familiar with nils since this is my first few days with Swift but I have worked with nulls in Java and C#. Are nils equivalent to nulls?
The correct output that I want should be returning like:
["Apple", "Orange"] // not necessarily in order
You haven't initialised the array. So this is how it should be :
public func getSometing() -> [String]?{
var myArray = [String]();
let keys = Array(complexes.keys);
for key in keys { myArray?.append(key); } // This may be the line of code that isn't properly working
return myArray;
}
Your getSometing() function should be like this,
public func getSometing() -> [String]?{
var myArray:[String] = [];
let keys = Array(complexes.keys);
for key in keys { myArray.append(key);}
return myArray;
}
your are not yet instantiated myArray. Try
var myArray:[String]? = [String]();
Problem is in your getSomething method:
// this is nil, there is no object in myArray, because you initialise it to nil
var myArray:[String]? = nil;
let keys = Array(complexes.keys);
for key in keys { myArray?.append(key); } // since myArray is nil, this does nothing
// and you return nil
return myArray;
So either initialise the array properly:
var myArray:[String]? = [] // this will create new empty array
Or do it even simpler:
public func getSomething() -> [String]?{
return Array(complexes.keys)
}
Consider these classes:
struct OrderedSet<T: Hashable> {}
class Exercise: Hashable {}
class StrengthExercise: Exercise {}
class CardioExercise: Exercise {}
I'd like to do the following:
var displayedExercises = OrderedSet<Exercise>() {
didSet {
self.tableView.reloadData()
}
}
var cardioExercises = OrderedSet<CardioExercise>()
var strengthExercises = OrderedSet<StrengthExercise>()
#IBAction func segmentControlChanged(segmentControl: UISegmentedControl) {
switch segmentControl.selectedSegmentIndex {
case 0: self.displayedExercises = self.strengthExercises
case 1: self.displayedExercises = self.cardioExercises
default: break
}
}
But I get this error:
Cannot assign value of type 'OrderedSet<StrengthExercise>' to type 'OrderedSet<Exercise>
I don't quite get this, since StrengthExercise is a subclass of Exercise and will have everything that OrderedSet<Exercise> expects.
The question(s)
Why is this error necessary?
How to I write something that achieves the functionality I'm going for?
Radar filed
rdar://23608799
Blog post on covariance and contravariance
https://www.mikeash.com/pyblog/friday-qa-2015-11-20-covariance-and-contravariance.html
I am afraid this is not currently possible as of Swift 2.1. Only the following conversions are supported
Built in collections types are covariant on their element type.
Conversions between function types are supported, exhibiting covariance in function result types and contravariance in function parameter types. (Cf. Xcode 7.1 Release Notes)
As Objective-C's generics support type variance, and given the progress made on function type conversions in Swift 2.1, I believe there is reason to believe type variance support will be added to Swift in the future. In the mean time, remember to file a radar, like jlieske has.
In the mean time you will have to copy the collection or use one of the builtin collection types.
Update since Swift become open source:
I believe the Complete generics section of Swift 3.0 Dev Roadmap indicates type variance will be addressed in 3.0. While type variance is not specifically called out, special cased exceptions in the standard library (which includes type variance) are.
As the OrderedSet<StrengthExercise> is of that specific type it cannot be assigned to the more general OrderedSet<Exercise>. Think about what would happen if you tried to append a Cardio exercise to that OrderedSet after assigning it.
The answer might be to modify append the contents of the strength exercises set to the exercise set rather than assign the whole typed set.
this should work
class Base {}
class A: Base {}
class B: Base {}
var arrBase: Array<Base> = []
var arrA: Array<A> = []
arrA.append(A())
var arrB: Array<B> = []
arrB.append(B())
arrBase = arrA // no error
arrBase = arrB // no error
... your trouble seems to be somewhere else in your code. can you show us your generic struct OrderedSet implementation ?? it seems like you are trying to do something like
class Base {}
class A: Base {}
let base = Base()
let a = A()
struct S<T:Base> {
var t: T
}
var s = S(t: base)
let sa = S(t: a)
//s = sa // error: cannot assign value of type 'S<A>' to type 'S<Base>'
let sb = S(t: a as Base)
s = sb
... this works
protocol P {
func whoAmI()->Void
}
class Base:P {
func whoAmI() {
print("I am Base")
}
}
class A: Base {
override func whoAmI() {
print("I am A")
}
}
let base = Base()
let a = A()
struct S<T: Base> {
var t: Base
}
var s = S(t: base)
let sa = S(t: a)
s = sa
s.t.whoAmI() // I am A
.... guys, build-in type or not
import Foundation
// Int and Double conforms to Hashable protocol
var a: Set<Int> = []
var b: Set<Double> = []
a = b // IMPOSSIBLE eventhough Set<T:Hashable> is build-in Swift type
... how to deal with OrderedSet
import Foundation
class Exercise: Hashable {
var name: String = ""
var hashValue: Int {
return name.hashValue
}
}
func ==(lhs: Exercise, rhs: Exercise) -> Bool {
return lhs.name == rhs.name
}
class StrengthExercise: Exercise {}
class CardioExercise: Exercise {}
var displayedExercises = Set<Exercise>()
let strengthExercises = Set<StrengthExercise>()
let cardioExercises = Set<CardioExercise>()
displayedExercises = strengthExercises
// OK, the question is how to implement OrderedSet<T:Hashable>
// ------------------------------------------------------------------------------------------
//
// OrderedSet.swift
// Weebly
//
// Created by James Richard on 10/22/14.
// Copyright (c) 2014 Weebly. All rights reserved.
//
// Slightly modified by user3441734 on 11/18/15
//
// original code OrderedSet is available under the MIT license
/// An ordered, unique collection of objects.
public struct OrderedSet<T: Hashable> {
private var contents = [T: Index]() // Needs to have a value of Index instead of Void for fast removals
private var sequencedContents = Array<UnsafeMutablePointer<T>>()
/**
Inititalizes an empty ordered set.
:return: An empty ordered set.
*/
public init() { }
/**
Initializes a new ordered set with the order and contents
of sequence.
If an object appears more than once in the sequence it will only appear
once in the ordered set, at the position of its first occurance.
:param: sequence The sequence to initialize the ordered set with.
:return: An initialized ordered set with the contents of sequence.
*/
public init<S: SequenceType where S.Generator.Element == T>(sequence: S) {
// FIXME: For some reason, Swift gives the error "Cannot convert the expression's type 'S' to type 'S'" with a regular for-in, so this is a hack to fix that.
var gen = sequence.generate()
while let object: T = gen.next() {
if contents[object] == nil {
contents[object] = contents.count
let pointer = UnsafeMutablePointer<T>.alloc(1)
pointer.initialize(object)
sequencedContents.append(pointer)
}
}
}
/**
Replace, remove, or retrieve an object in the ordered set.
When setting an index to nil the object will be removed. If
it is not the last object in the set, all subsequent objects
will be shifted down one position.
When setting an index to another object, the existing object
at that index will be removed. If you attempt to set an index
that does not currently have an object, this is a no-op.
:param: index The index to retrieve or set.
:return: On get operations, the object at the specified index, or nil
if no object exists at that index.
*/
public subscript(index: Index) -> T {
get {
return sequencedContents[index].memory
}
set {
contents[sequencedContents[index].memory] = nil
contents[newValue] = index
sequencedContents[index].memory = newValue
}
}
/**
Locate the index of an object in the ordered set.
It is preferable to use this method over the global find() for performance reasons.
:param: object The object to find the index for.
:return: The index of the object, or nil if the object is not in the ordered set.
*/
public func indexOfObject(object: T) -> Index? {
if let index = contents[object] {
return index
}
return nil
}
/// The number of objects contained in the ordered set.
public var count: Int {
return contents.count
}
/// Whether the ordered set has any objects or not.
public var isEmpty: Bool {
return count == 0
}
/**
Tests if the ordered set contains an object or not.
:param: object The object to search for.
:return: true if the object exists in the ordered set, otherwise false.
*/
public func contains(object: T) -> Bool {
return contents[object] != nil
}
/**
Appends an object to the end of the ordered set.
:param: object The object to be appended.
*/
mutating public func append(object: T) {
if contents[object] != nil {
return
}
contents[object] = contents.count
let pointer = UnsafeMutablePointer<T>.alloc(1)
pointer.initialize(object)
sequencedContents.append(pointer)
}
/**
Appends a sequence of objects to the end of the ordered set.
:param: objects The objects to be appended.
*/
mutating public func appendObjects<S: SequenceType where S.Generator.Element == T>(objects: S) {
var gen = objects.generate()
while let object: T = gen.next() {
append(object)
}
}
/**
Removes an object from the ordered set.
If the object exists in the ordered set, it will be removed.
If it is not the last object in the ordered set, subsequent
objects will be shifted down one position.
:param: object The object to be removed.
*/
mutating public func remove(object: T) {
if let index = contents[object] {
contents[object] = nil
sequencedContents[index].dealloc(1)
sequencedContents.removeAtIndex(index)
for (object, i) in contents {
if i < index {
continue
}
contents[object] = i - 1
}
}
}
/**
Removes the given objects from the ordered set.
:param: objects The objects to be removed.
*/
mutating public func removeObjects<S: SequenceType where S.Generator.Element == T>(objects: S) {
var gen = objects.generate()
while let object: T = gen.next() {
remove(object)
}
}
/**
Removes an object at a given index.
This method will cause a fatal error if you attempt to move an object to an index that is out of bounds.
:param: index The index of the object to be removed.
*/
mutating public func removeObjectAtIndex(index: Index) {
if index < 0 || index >= count {
fatalError("Attempting to remove an object at an index that does not exist")
}
remove(sequencedContents[index].memory)
}
/**
Removes all objects in the ordered set.
*/
mutating public func removeAllObjects() {
contents.removeAll()
sequencedContents.removeAll()
}
/**
Return an OrderedSet containing the results of calling
`transform(x)` on each element `x` of `self`
:param: transform A closure that is called for each element in the ordered set.
The result of the closure is appended to the new ordered set.
:result: An ordered set containing the result of `transform(x)` on each element.
*/
public func map<U: Hashable>(transform: (T) -> U) -> OrderedSet<U> {
var result = OrderedSet<U>()
for object in self {
result.append(transform(object))
}
return result
}
/// The first object in the ordered set, or nil if it is empty.
public var first: T? {
return count > 0 ? self[0] : nil
}
/// The last object in the ordered set, or nil if it is empty.
public var last: T? {
return count > 0 ? self[count - 1] : nil
}
/**
Swaps two objects contained within the ordered set.
Both objects must exist within the set, or the swap will not occur.
:param: first The first object to be swapped.
:param: second The second object to be swapped.
*/
mutating public func swapObject(first: T, withObject second: T) {
if let firstPosition = contents[first] {
if let secondPosition = contents[second] {
contents[first] = secondPosition
contents[second] = firstPosition
sequencedContents[firstPosition].memory = second
sequencedContents[secondPosition].memory = first
}
}
}
/**
Tests if the ordered set contains any objects within a sequence.
:param: sequence The sequence to look for the intersection in.
:return: Returns true if the sequence and set contain any equal objects, otherwise false.
*/
public func intersectsSequence<S: SequenceType where S.Generator.Element == T>(sequence: S) -> Bool {
var gen = sequence.generate()
while let object: T = gen.next() {
if contains(object) {
return true
}
}
return false
}
/**
Tests if a the ordered set is a subset of another sequence.
:param: sequence The sequence to check.
:return: true if the sequence contains all objects contained in the receiver, otherwise false.
*/
public func isSubsetOfSequence<S: SequenceType where S.Generator.Element == T>(sequence: S) -> Bool {
for (object, _) in contents {
if !sequence.contains(object) {
return false
}
}
return true
}
/**
Moves an object to a different index, shifting all objects in between the movement.
This method is a no-op if the object doesn't exist in the set or the index is the
same that the object is currently at.
This method will cause a fatal error if you attempt to move an object to an index that is out of bounds.
:param: object The object to be moved
:param: index The index that the object should be moved to.
*/
mutating public func moveObject(object: T, toIndex index: Index) {
if index < 0 || index >= count {
fatalError("Attempting to move an object at an index that does not exist")
}
if let position = contents[object] {
// Return if the client attempted to move to the current index
if position == index {
return
}
let adjustment = position < index ? -1 : 1
let range = index < position ? index..<position : position..<index
for (object, i) in contents {
// Skip items not within the range of movement
if i < range.startIndex || i > range.endIndex || i == position {
continue
}
let originalIndex = contents[object]!
let newIndex = i + adjustment
let firstObject = sequencedContents[originalIndex].memory
let secondObject = sequencedContents[newIndex].memory
sequencedContents[originalIndex].memory = secondObject
sequencedContents[newIndex].memory = firstObject
contents[object] = newIndex
}
contents[object] = index
}
}
/**
Moves an object from one index to a different index, shifting all objects in between the movement.
This method is a no-op if the index is the same that the object is currently at.
This method will cause a fatal error if you attempt to move an object fro man index that is out of bounds
or to an index that is out of bounds.
:param: index The index of the object to be moved.
:param: toIndex The index that the object should be moved to.
*/
mutating public func moveObjectAtIndex(index: Index, toIndex: Index) {
if ((index < 0 || index >= count) || (toIndex < 0 || toIndex >= count)) {
fatalError("Attempting to move an object at or to an index that does not exist")
}
moveObject(self[index], toIndex: toIndex)
}
/**
Inserts an object at a given index, shifting all objects above it up one.
This method will cause a fatal error if you attempt to insert the object out of bounds.
If the object already exists in the OrderedSet, this operation is a no-op.
:param: object The object to be inserted.
:param: atIndex The index to be inserted at.
*/
mutating public func insertObject(object: T, atIndex index: Index) {
if index > count || index < 0 {
fatalError("Attempting to insert an object at an index that does not exist")
}
if contents[object] != nil {
return
}
// Append our object, then swap them until its at the end.
append(object)
for i in Range(start: index, end: count-1) {
swapObject(self[i], withObject: self[i+1])
}
}
/**
Inserts objects at a given index, shifting all objects above it up one.
This method will cause a fatal error if you attempt to insert the objects out of bounds.
If an object in objects already exists in the OrderedSet it will not be added. Objects that occur twice
in the sequence will only be added once.
:param: objects The objects to be inserted.
:param: atIndex The index to be inserted at.
*/
mutating public func insertObjects<S: SequenceType where S.Generator.Element == T>(objects: S, atIndex index: Index) {
if index > count || index < 0 {
fatalError("Attempting to insert an object at an index that does not exist")
}
var addedObjectCount = 0
// FIXME: For some reason, Swift gives the error "Cannot convert the expression's type 'S' to type 'S'" with a regular for-in, so this is a hack to fix that.
var gen = objects.generate()
// This loop will make use of our sequncedContents array to update the contents dictionary's
// values. During this loop there will be duplicate values in the dictionary.
while let object: T = gen.next() {
if contents[object] == nil {
let seqIdx = index + addedObjectCount
let element = UnsafeMutablePointer<T>.alloc(1)
element.initialize(object)
sequencedContents.insert(element, atIndex: seqIdx)
contents[object] = seqIdx
addedObjectCount++
}
}
// Now we'll remove duplicates and update the shifted objects position in the contents
// dictionary.
for i in index + addedObjectCount..<count {
contents[sequencedContents[i].memory] = i
}
}
}
extension OrderedSet: MutableCollectionType {
public typealias Index = Int
public typealias _Element = T
public typealias Generator = OrderedSetGenerator<T>
public func generate() -> Generator {
return OrderedSetGenerator(set: self)
}
public var startIndex: Int {
return 0
}
public var endIndex: Int {
return count
}
}
public struct OrderedSetGenerator<T: Hashable>: GeneratorType {
public typealias Element = T
private var generator: IndexingGenerator<Array<UnsafeMutablePointer<T>>>
public init(set: OrderedSet<T>) {
generator = set.sequencedContents.generate()
}
mutating public func next() -> Element? {
return generator.next()?.memory
}
}
public func +<T: Hashable, S: SequenceType where S.Generator.Element == T> (lhs: OrderedSet<T>, rhs: S) -> OrderedSet<T> {
var joinedSet = lhs
joinedSet.appendObjects(rhs)
return joinedSet
}
public func +=<T: Hashable, S: SequenceType where S.Generator.Element == T> (inout lhs: OrderedSet<T>, rhs: S) {
lhs.appendObjects(rhs)
}
public func -<T: Hashable, S: SequenceType where S.Generator.Element == T> (lhs: OrderedSet<T>, rhs: S) -> OrderedSet<T> {
var purgedSet = lhs
purgedSet.removeObjects(rhs)
return purgedSet
}
public func -=<T: Hashable, S: SequenceType where S.Generator.Element == T> (inout lhs: OrderedSet<T>, rhs: S) {
lhs.removeObjects(rhs)
}
extension OrderedSet: Equatable { }
public func ==<T: Hashable> (lhs: OrderedSet<T>, rhs: OrderedSet<T>) -> Bool {
if lhs.count != rhs.count {
return false
}
for object in lhs {
if lhs.contents[object] != rhs.contents[object] {
return false
}
}
return true
}
// ------------------------------------------------------------------------------------------
// finaly what do you want
var displayedExercises1 = OrderedSet<Exercise>()
let strengthExercises1 = OrderedSet<StrengthExercise>()
let cardioExercises1 = OrderedSet<CardioExercise>()
displayedExercises = strengthExercises
How can I check if a property is an Array (of any type)? This code always only prints "Worker". Is there a way (dynamically) to know if a property is an Array without inform the type?
final class Worker: NSObject {
var id: Int?
var array: Array<Worker>?
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let worker = Worker()
worker.id = Int(2) as Int?
worker.array = [Worker(),Worker(),Worker()]
let mirror = reflect(worker)
for i in 0..<mirror.count {
let (name, childMirror) = mirror[i]
if childMirror.disposition == .Optional {
let (newName,subChildMirror) = childMirror[0]
if subChildMirror.valueType is Array<AnyClass>.Type {
println("AnyClass")
}
if subChildMirror.valueType is Array<AnyObject>.Type {
println("AnyObject")
}
if subChildMirror.valueType is Array<Any>.Type {
println("Any")
}
if subChildMirror.valueType is Array<NSObject>.Type {
println("NSObject")
}
if subChildMirror.valueType is Array<Worker>.Type {
println("Worker")
}
}
}
}
}
Ps.: I need to deal with Array<>
An array of any type can always be casted to a NSArray. So you could check if it's an array with code like this:
if _ = subChildMirror.valueType as? NSArray {
println("Array")
}
It's also possible to dynamically get the type of the objects of that array. In my EVReflection library I do something similar. I extended the Array in order to get a new element of an object what should be in that Array. In your case you could then get the .dynamicType from dat object.
So the code would become:
let arrayType = worker.array.getTypeInstance().dynamicType
Here is the Array extension
extension Array {
/**
Get the type of the object where this array is for
:returns: The object type
*/
public func getTypeInstance<T>(
) -> T {
let nsobjectype : NSObject.Type = T.self as! NSObject.Type
let nsobject: NSObject = nsobjectype.init()
return nsobject as! T
}
}
Is it possible to count all properties who are not nil?
For example:
class test {
var val1:Int?
var val2:Int?
var val3:Int?
var val4:Int?
var val5:Int?
}
var test = test()
test.val1 = 1
test.val2 = 2
How to find out that 2 properties are set? I could check for each one with (!= nil) - but is there an easier (and better) way?
You can do this manually, with a convenience method:
func numberOfNonNil() -> Int {
let vals = [val1, val2, val3, val4, val5]
return flatMap { $0 }.count
}
flatMap(_:) takes a closure that takes a single element of the array and returns an optional value (Element -> T?), and returns the result of applying that closure to each element of the array, with nil values ignored.
The only way to make this simpler would be to store your values as an array of optional Ints in the first place:
class Test {
var vals: [Int?]
}
You can then still access each individual value using the array subscript notation (let val2 = vals[1]). You could then just use the second line in the convenience method above (filter then count) to get the number of non-nil values:
let nonNilCount = vals.flatMap { $0 }.count
If your values are of different types, this approach will still work if you cast the array to a type that encompasses all the different types:
class Test {
var val1: Int?
var val2: Double
var val3: String
var val4: MyRandomClass?
func numberOfNonNil() -> Int {
let vals = [val1, val2, val3, val4, val5] as [Any?]
return flatMap { $0 }.count
}
}
This works because all the values can be expressed as the type Any?.
I don't think there's a way to do this, but you can implement your own function like this:
class test {
var val1:Int?
var val2:Int?
var val3:Int?
var val4:Int?
var val5:Int?
func setVarsCount() -> Int {
var setVariablesCount = 0
if val1 != nil {
setVariablesCount++
}
if val2 != nil {
setVariablesCount++
}
if val3 != nil {
setVariablesCount++
}
if val4 != nil {
setVariablesCount++
}
if val5 != nil {
setVariablesCount++
}
return setVariablesCount
}
}
#Stuarts answer is good, but you have to know the properties of the class, and if you add another property to the class, you also have to modify your method. To avoid this problem, you can use reflection, like
Swift 1.2:
func numberOfOptionalProperties() -> Int {
let mirror = reflect(self)
var numberOfOptionalProperties = 0
for index in 0..<mirror.count {
if mirror[index].1.disposition == .Optional {
++numberOfOptionalProperties
}
}
return numberOfOptionalProperties
}
Swift 2.0:
func numberOfOptionalProperties() -> Int {
return Mirror(reflecting: self).children.reduce(0) {
Mirror(reflecting: $1.value).displayStyle == .Optional ? $0 + 1 : $0
}
}
I would like to create a typed map (Dictionary) class to meet the following requirements:
func testMap() {
var map = ActivitiesMap()
var activity = Activity()
activity.title = "Activity 1"
activity.uuid = "asdf1234"
map[activity.uuid] = activity
for (key, mapActivity) in map {
logger.debug("ACTIVITY MAP: \(key)=\(mapActivity)")
}
}
In short, I want this class to both be a dictionary such that it can be used in the for loop, however I want to ensure the keys are strings and the values are Activity objects.
I tried many variations of inheriting from Dictionary or typing the class, but so far it's resulted in multiple errors.
EDIT:
I don't think a simple generic dictionary will work, such as String:Activity. I want to have extra methods in the ActivityMap class, such as getAllActivitiesBetweenDates().
I need an actual class definition, not a generic dictionary expression.
You can make it looks like dictionary by implement subscript operator
And conform to Sequence protocol to support for-in loop
struct ActivitiesMap : Sequence {
var map = [String:Activity]()
subscript(key: String) -> Activity? {
get {
return map[key]
}
set(newValue) {
map[key] = newValue
}
}
func generate() -> GeneratorOf<(String, Activity)> {
var gen = map.generate()
return GeneratorOf() {
return gen.next()
}
}
// I can't find out type of map.generator() now, if you know it, you can do
//func generate() -> /*type of map.generator()*/ {
// return map.generate();
//}
}
This works for me. Not sure what is in your ActivitiesMap class, but just typed a Dictionary
class Activity{
var title:String = "";
var uuid: String = "";
}
func testMap() {
//var map = ActivitiesMap()
var map: Dictionary< String, Activity> = Dictionary< String, Activity>();
var activity = Activity()
activity.title = "Activity 1"
activity.uuid = "asdf1234"
map[activity.uuid] = activity
for (key, mapActivity) in map {
println("ACTIVITY MAP: \(key)=\(mapActivity)")
}
}
testMap();
This is my output:
ACTIVITY MAP: asdf1234=C11lldb_expr_08Activity (has 2 children)
class Activity {
var title=""
var id=""
init(id:String, title:String) { self.id=id; self.title = title }
}
var activities = [String:Activity]()
let a1 = Activity(id:"a1", title:"title1")
let a2 = Activity(id:"a2", title:"title2")
let a3 = Activity(id:"a3", title:"title3")
activities[a1.id] = a1
activities[a2.id] = a2
activities[a3.id] = a3
for (id,activity) in activities {
println("id: \(id) - \(activity.title)")
}
should print
id: a2 - title2
id: a3 - title3
id: a1 - title1
(key order not guaranteed to be the same)
You can use typealias keyword to define nice name of any type.
Here is how it can be used for your code:
class Activity { /* your code */ }
typealias ActivityMap = Dictionary<String, Activity>
var activityDict = ActivityMap()
And to support custom functions you can write an extension, example bellow:
extension Dictionary {
func getAllActivitiesBetweenDates(fromDate:NSDate, toDate:NSDate) -> Array<Activity>
// your code
return []
}
}
Usage:
let matchedActivities = activityDict.getAllActivitiesBetweenDates(/*date*/, /*date*/)