Error: Cannot subscript a value of type 'X' with ...' - swift

Error: Cannot subscript a value of type '[CustomClass]' with an index of type '(safe: Int)'
class CustomClass {
let value: String
init(value: String) {
self.value = value
}
}
extension Collection {
subscript(safe: Int) -> Element? {
if safe > count-1 {
return nil
}
return self[safe]
}
}
let steps: [CustomClass] = []
if let step = steps[safe: 4] { // error here
}
Why is this happening?

Note that besides the subscript parameter issue already mentioned in comments by #Hamish there are a few other issues in your code: ArraySlice also conforms to RandomAccessCollection so just checking the array count doesn't guarantee it is a safe index. You should add a guard statement to check if the indices property contains the Index. You should also change your subscript parameter to Index instead of Int:
class CustomClass {
let value: Int
init(value: Int) {
self.value = value
}
}
extension Collection {
subscript(safe index: Index) -> Element? {
guard indices.contains(index) else {
return nil
}
return self[index]
// or simply
// return indices.contains(index) ? self[index] : nil
}
}
Playground testing:
let steps = [CustomClass(value: 0),CustomClass(value: 1),CustomClass(value: 2),CustomClass(value: 3),CustomClass(value: 4),CustomClass(value: 5),CustomClass(value: 6)]
if let step6 = steps[safe: 6] {
print(step6.value) // 6
}
let stepsSlice = steps[0...4]
let step6 = stepsSlice[safe: 6]
print(step6?.value) // nil

Related

How can I implement a CountedSet (NSCountedSet) in Swift?

Create a generic CountedSet struct that is constrained to Hashable elements. A counted set is an unordered collection of unique elements that may appear more than once in the collection. Use a private dictionary as your backing storage for set members and their counts.
struct CountedSet<Element> {
private(set) var elements: [Element]
mutating func insert(_ element: Element) {
elements.append(element)
}
mutating func remove() -> Element? {
guard elements.isEmpty == false else { return nil}
return elements.removeFirst()
}
subscript(_ member: Element) -> Int {
return 0
}
}
I don't understand what the real objective is here. The instructions are very confusing at least to me.
1) Make your generic struct element conform to Hashable, this is necessary because the dictionary keys are required to conform to Hashable.
struct CountedSet<Element: Hashable>
2) The backing storage you have used is an ordered array, not a dictionary and you need to initialize it with an empty one.
private(set) var elements: [Element: Int] = [:]
3) Your subscript method you need to return the count for the counted set member or zero if it is nil.
return elements[member] ?? 0
4) Your Insert and Remove methods need to first check the count of a member in the backing dictionary before adding or removing an element from it.
So your CountedSet should look like this:
struct CountedSet<Element: Hashable> {
private(set) var elements: [Element: Int] = [:]
mutating func insert(_ member: Element) {
elements[member, default: 0] += 1
}
mutating func remove(_ member: Element) -> Element? {
guard var count = elements[member], count > 0 else { return nil }
count -= 1
elements[member] = count == 0 ? nil : count
return member
}
subscript(_ member: Element) -> Int {
elements[member] ?? 0
}
}
var countedSet = CountedSet<Int>()
countedSet.insert(3)
countedSet.insert(3)
countedSet.insert(4)
countedSet.elements // [4: 1, 3: 2]
countedSet.remove(4)
countedSet.elements // [3: 2]
countedSet.remove(4) // nil
Expanding on that you can also make your CountedSet conform to ExpressibleByArrayLiteral to allow you to initialize your CountedSet with an array and CustomStringConvertible to allow you to print its elements:
extension CountedSet: ExpressibleByArrayLiteral, CustomStringConvertible {
typealias ArrayLiteralElement = Element
init<S: Sequence>(_ sequence: S) where S.Element == Element {
self.elements = sequence.reduce(into: [:]) { $0[$1, default: 0] += 1 }
}
init(arrayLiteral elements: Element...) { self.init(elements) }
var description: String { .init(describing: elements) }
}
var countedSet: CountedSet = [1,2,2,3,3,3,4,4,5,5,5]
print(countedSet) // "[5: 3, 2: 2, 3: 3, 4: 2, 1: 1]\n"

Storing Generic Objects in a Heterogeneous Array and retrieving object parameter as the correct type

Ahoy everyone,
I have recently been trying to implement a Node based graph system that passes data between nodes using plugs. Similar to many of the 3D applications like houdini and maya.
I have written a similar system before using Python, and wanted to try this with Swift as my first learning excersise. Boy did I jump into the deep end on this one.
I am stuck now with Swifts Arrays, as I would like to store a list of Generic plugs.
Each plug can have its own value type float, int, color, string, Vector Matrix.
I have read up about Type Erasers and opaque types, but still cant seem to get my values our of a list in a way that I can perform some arithmetic on them.
All and any help that might put me in the direction would be greatly appreciated :D
import Foundation
import MetalKit
protocol genericPlug {
associatedtype T
func GetValue() -> T
}
class Plug<T>:genericPlug{
var _value:T?
var value:T {
get{GetValue()}
set(val){
value = val
}
}
func GetValue() -> T{
return _value!
}
init(_ newValue:T){
_value=newValue
}
}
class Node{
var plugs:[genericPlug] = []
init(){
var p1 = Plug<Int>(0)
var p2 = Plug(vector2(1.2, 3.1))
var p3 = Plug([0.0, 3.1, 0.6, 1])
plugs.append(p1)
plugs.append(p2)
plugs.append(p3)
}
func execute(){
// will access the plugs in the array and perform some sort of calculations on them.
plugs[0].value + 1 // should equal 1
plugs[1].value.x + 0.8 // should have x=2.0 y=3.1
plugs[2].value[1] - 0.1 // should equal 3.0
}
}
Thanks everyone
Use a generic something to extract what you need. Your options are methods and subscripts.
protocol PlugValue {
init()
}
extension Int: PlugValue { }
extension Float: PlugValue { }
extension Double: PlugValue { }
extension SIMD3: PlugValue where Scalar == Int32 { }
struct Plug<Value: PlugValue> {
var value: Value
init(_ value: Value) {
self.value = value
}
}
protocol AnyPlug {
var anyValue: PlugValue { get }
}
extension AnyPlug {
subscript<Value: PlugValue>(type: Value.Type = Value.self) -> Value {
anyValue as? Value ?? .init()
}
func callAsFunction<Value: PlugValue>(_ type: Value.Type = Value.self) -> Value {
anyValue as? Value ?? .init()
}
}
extension Plug: AnyPlug {
var anyValue: PlugValue { value }
}
let plugs: [AnyPlug] = [
Plug(1),
Plug(2.3 as Float),
Plug(4.5),
Plug([6, 7, 8] as SIMD3)
]
plugs[0][Int.self] // 1
plugs[1][Double.self] // 0
plugs[1][] as Float // 2.3
let double: Double = plugs[2]() // 4.5
plugs[3](SIMD3.self).y // 7
With the array of protocols, do you have to down cast them into their Plug when retrieving them every time?
Essentially. This is true of all heterogenous sequences. Here are your options:
extension Array: PlugValue where Element: PlugValue { }
let plug: AnyPlug = Plug([0.1, 1.1, 2.1])
(plug as? Plug<[Double]>)?.value[1]
(plug.anyValue as? [Double])?[1]
extension Plug {
enum Error: Swift.Error {
case typeMismatch
}
}
extension AnyPlug {
func callAsFunction<Value: PlugValue, Return>(_ closure: (Value) -> Return) throws {
guard let value = anyValue as? Value
else { throw Plug<Value>.Error.typeMismatch }
closure(value)
}
}
try plug { (doubles: [Double]) in doubles[1] } // 1.1
try plug { ($0 as [Double])[1] } // 1.1
try plug { $0 as Int } // <Swift.Int>.Error.typeMismatch
I managed to find a solution that worked for my needs.
I am still looking at finding a better way to handle getting the data and their correct type.
import Foundation
import MetalKit
// Creating the PlugType Enum
enum PlugType{
case Integer(Int?)
case Float_(Float?)
case Double_(Double?)
case Vector3(simd_int3)
// default types
static func IntegerType() -> PlugType{ return PlugType.Integer(nil)}
static func FloatType() -> PlugType{ return PlugType.Float_(nil)}
static func DoubleType() -> PlugType{ return PlugType.Double_(nil)}
}
// Implements a way to retrieve the correct value type
extension PlugType{
var IntegerValue: Int{
switch self{
case .Integer(let value):
return value ?? 0
default:
return 0
}
}
var FloatValue: Float{
switch self
{
case .Float_(let value):
return value ?? 0
default:
return 0
}
}
var DoubleValue: Double{
switch self
{
case .Double_(let value):
return value ?? 0
default:
return 0
}
}
}
// Get the string representation of the PlugType
extension PlugType {
var typeName: String{
switch self {
case .Integer: return "Integer"
case .Float_: return "Float"
case .Double_: return "Double"
case .Vector3: return "Vector3"
}
}
var swiftType: Any.Type {
switch self {
case .Integer: return Int.self
case .Float_: return Float.self
case .Double_: return Double.self
case .Vector3: return simd_int3.self
}
}
}
class Plug{
var _value:PlugType?
var type:String? { get{ return _value?.typeName } }
init(_ newValue:PlugType){
_value = newValue
}
func geee<T>(_ input:T) -> T{
switch type {
case "Integer":
return getVal(_value!.IntegerValue) as! T
case "Double":
return getVal(_value!.DoubleValue) as! T
default:
return 0 as! T
}
}
func getVal(_ val:Int) -> Int {
return val
}
func getVal(_ val:Float) -> Float {
return val
}
func getVal(_ val:Double) -> Double {
return val
}
}
var plugs:[Plug] = []
var p1 = Plug(PlugType.Integer(2))

How to filter objects of certain type from [AnyObject] array

Is it possible to filter an array of [AnyObject] to yield all elements of a given type, and none other?
I can do it if the type is known at compile time:
class MyClass1: CustomStringConvertible {
var value: Int
var description: String {
return "MyClass1: \(value)"
}
init(_ value: Int) {
self.value = value
}
}
class MyClass2: CustomStringConvertible {
var value: Int
var description: String {
return "MyClass1: \(value)"
}
init(_ value: Int) {
self.value = value
}
}
class MySubClass1: MyClass1 {
override var description: String {
return "MySubClass1: \(value)"
}
}
let a1 = MySubClass1(1)
let a2 = MySubClass1(2)
let b1 = MyClass1(3)
let b2 = MyClass2(4)
let array: [AnyObject] = [a1, b1, a2, b2]
func getClass1ObjectsFromArray(_ array: [AnyObject]) -> [MyClass1] {
return array.compactMap( { $0 as? MyClass1 })
}
func getSubClass1ObjectsFromArray(_ array: [AnyObject]) -> [MySubClass1] {
return array.compactMap( { $0 as? MySubClass1 })
}
print(getClass1ObjectsFromArray(array))
print(getSubClass1ObjectsFromArray(array))
Prints:
[MySubClass1: 1, MyClass1: 3, MySubClass1: 2]
[MySubClass1: 1, MySubClass1: 2]
For every type I want to filter on, I had to write a separate function. This looks ugly to me, and will not work when the type to be selected for is only known at run time.
Question:
Is there a generic way to write such a function? Preferably something like:
func getObjectsOfType(_ type: TypeExpression, fromArray array: [AnyObject])
-> [TypeExpression] {
...
}
Or any other way to achieve this?
Thanks for any help!
I think you could use something like this...
let filteredArray = array.compactMap { $0 as? RequiredType }
This will filter the array and return a typed array containing only the type you want.
Caveat
Having said that. In Swift you should be avoiding heterogeneous arrays where possible. Arrays should really only contain one type of item.
A bit of code testing...
Tested in Playground...
let array: [Any] = [1, "hello", 3, 3.1415, "world"]
let filteredArray = array.compactMap { $0 as? String }
filteredArray
Output:
filteredArray = ["hello", "world"]
👍🏻
Edit 1
You could also create a generic function something like this...
func filter<T>(array: [Any]) -> [T] {
return array.compactMap { $0 as? T }
}
let filteredArray: [String] = filter(array: array)
This will then filter based on the type of the output array that you want.
I'm not sure what you mean by only knowing the type you want at run time. Can you give a more concrete example of what you mean?
Edit 2
Another possibility is a generic function like this...
func filter<T>(array: [Any], byType typeObject: T) -> [T] {
return array.compactMap { $0 as? T }
}
let filteredArray = filter(array: array, byType: "some string")
This uses the type information of the second parameter to filter the array by that type of item.
Edit 3
If you don't like passing in an instance of the type then you can pass the type itself...
func filter<T>(array: [Any], byType typeObject: T.Type) -> [T] {
return array.compactMap { $0 as? T }
}
let filteredArray = filter(array: array, byType: String.self)
But I'm not sure what more you're getting from this than just filtering by string in the first place?

swift affect value to inout generic variable

I want to simplify this piece of code with a T variable but could not succeed in compiling it. Hope could you give me the way.
here is the "duplicate" code I want to rewrite :
func getIntegerValue (listValues: [Any], numValueToRead: Int, readValue: inout Int) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues [numValueToRead]
if type (of: value) == type(of: readValue) {
readValue = value as! Int
return true
} else {
return false
}
}
func getStringValue (listValues: [Any], numValueToRead: Int, readValue: inout String) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues [numValueToRead]
if type (of: value) == type(of: readValue) {
readValue = value as! String
return true
} else {
return false
}
}
Here is the code I wrote but do not compile :
func getValue <T> (listValues: [Any], numValueToRead: Int, readValue: inout T) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues [numValueToRead]
if type (of: value) == type(of: readValue) {
switch value {
case let integerValue as Int:
readValue = integerValue
case let stringValue as String:
readValue = stringValue
default:
return false
}
return true
} else {
return false
}
}
for those affectations I got those compilation errors :
readValue = integerValue -> 'Int' is not convertible to 'T'
readValue = stringValue -> 'String' is not convertible to 'T'
Is there a way to synthetise my two functions with a unique one using generics ?
You theoretically could make it compile by adding forced casts, since you already know that value has the type T:
case let integerValue as Int:
readValue = integerValue as! T
case let stringValue as String:
readValue = stringValue as! T
But the far better solution is to use a conditional cast (as? T) and
conditional binding (if let):
func getValue<T>(listValues: [Any], numValueToRead: Int, readValue: inout T) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues[numValueToRead]
if let tvalue = value as? T {
readValue = tvalue
return true
} else {
return false
}
}
which then works for arbitrary types, not only Int and String.
A “swiftier” way would be return an optional value (with nil
indicating "no value"). The code can then be simplified to
func getValue<T>(listValues: [Any], numValueToRead: Int) -> T? {
guard listValues.indices.contains(numValueToRead) else {
return nil
}
return listValues[numValueToRead] as? T
}
This should work:
func getValue <T> (listValues: [Any], numValueToRead: Int, readValue: inout T) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues [numValueToRead]
if type (of: value) == type(of: readValue) {
if let genericValue = value as? T {
readValue = genericValue
return true
}
return false
} else {
return false
}
}
At first sight, the function is wrongly named. You cannot call function getValue when it returns bool... I would call it transform or modify or something other than get value, because you are NOT getting value.
I think this method suits better your needs, not tested tought it should work.
func transformValue<T>(from listValues: [Any], numValueToRead: Int, readValue: inout T?) throws -> Bool {
// Guard suits better this case...
guard numValueToRead > 0 || numValueToRead < listValues.count else { return false }
let value = listValues[numValueToRead]
if type (of: value) == type(of: readValue) {
guard let value = value as? T else {
throw NSError(
domain: "Smth",
code: 1,
userInfo: ["Description": "Failed to cast to generic type T"]
)
}
readValue = value as? T
return true
}
return false // No need to call else...
}
Explenation: Returning optional generic type T is much safer. You try to cast it, you fail and you throw error that something went wrong. In my opinion saving force casts with throwing errors is much more safer approach, you know what went wrong and so.
As #MartinR pointed out, returning a nil value instead of an inout+Bool combination gives the same results, but with less, and more readable code. This is the path Swift also took when importing most of the NSError ** methods from Objective-C (i.e. dropped the last parameter, imported them as throwable functions).
These being said, another approach would be to add an extension over Array for extracting the value:
extension Array {
subscript<T>(_ index: Int, as type: T.Type) -> T? {
guard 0..<count ~= index else { return nil }
return self[index] as? T
}
}
let arr: [Any] = [1, "two", 3, "four"]
arr[1, as: String.self] // two
arr[2, as: String.self] // nil

Getting a random element in array by a generic function

func ramElment<X, T: CollectionType >(list: T) -> X {
let len = UInt32(list.count)
let element = arc4random_uniform(len)
return list[element]
}
it pops up:
error: cannot invoke initializer for type UInt32 with an argument list of type '(T.Index.Distance)'
let len = UInt32(list.count)
I have checked the T.Index.Distance is Int type. but why can't i change the type to UInt32?
thanks!
The Index of a CollectionType is
a ForwardIndexType:
public protocol ForwardIndexType : _Incrementable {
// ...
typealias Distance : _SignedIntegerType = Int
// ...
}
This means that the associated type Distance must conform to _SignedIntegerType, and (by default) is Int unless declared (or inferred)
otherwise.
Example: The following is a valid type conforming to ForwardIndexType,
with Distance == Int16:
struct MyIndex : ForwardIndexType {
var value : Int16
func advancedBy(n: Int16) -> MyIndex {
return MyIndex(value: value + n)
}
func distanceTo(end: MyIndex) -> Int16 {
return end.value - value
}
func successor() -> MyIndex {
return MyIndex(value: value + 1)
}
}
func ==(lhs : MyIndex, rhs : MyIndex) -> Bool {
return lhs.value == rhs.value
}
And here is a (for demonstration purposes, otherwise pretty useless)
type conforming to CollectionType with Index == MyIndex,
Index.Distance == Int16:
struct MyCollectionType : CollectionType {
var startIndex : MyIndex { return MyIndex(value: 0) }
var endIndex : MyIndex { return MyIndex(value: 3) }
subscript(position : MyIndex) -> String {
return "I am element #\(position.value)"
}
}
Example:
let coll = MyCollectionType()
for elem in coll {
print(elem)
}
/*
I am element #0
I am element #1
I am element #2
*/
But we can also define a forward index type without declaring
any Distance type, and using the default protocol implementations
for advancedBy() and distanceTo():
struct MyOtherIndex : ForwardIndexType {
var value : Int16
func successor() -> MyOtherIndex {
return MyOtherIndex(value: value + 1)
}
}
func ==(lhs : MyOtherIndex, rhs : MyOtherIndex) -> Bool {
return lhs.value == rhs.value
}
Now MyOtherIndex.Distance == Int because that is the default type
as defined in ForwardIndexType.
So how does this apply to your function?
You cannot assume that
Index.Distance is Int for an arbitrary collection.
You can restrict the function to collection types with
Index.Distance == Int:
func randomElement<T: CollectionType where T.Index.Distance == Int>(list: T)
But you can also utilize that _SignedIntegerType can be converted to and from IntMax:
func randomElement<T: CollectionType>(list: T) -> T.Generator.Element {
let len = UInt32(list.count.toIntMax())
let element = IntMax(arc4random_uniform(len))
return list[list.startIndex.advancedBy(T.Index.Distance(element))]
}
Note also that the return type is determined as T.Generator.Element,
and cannot be an arbitrary generic type X.
This function works with arbitrary collections, for example Array, ArraySlice, or String.CharacterView:
let array = [1, 1, 2, 3, 5, 8, 13]
let elem1 = randomElement([1, 2, 3])
let slice = array[2 ... 3]
let elem2 = randomElement(slice)
let randomChar = randomElement("abc".characters)
but also with the above custom collection type:
let mc = MyCollectionType()
let r = randomElement(mc)
In the title of your question you talk about Array. But in your code you are declaring the input param as a CollectionType.
If you really want to receive a Generic Array param then this is the code
func randomElement<T>(list: [T]) -> T {
let len = UInt32(list.count)
let random = Int(arc4random_uniform(len))
return list[random]
}
This is the simplest example what you want.
extension CollectionType where Index.Distance == Int {
func randomElement() -> Self.Generator.Element {
let randomIndex = Int(arc4random_uniform(UInt32(count)))
return self[startIndex.advancedBy(randomIndex)]
}
}