1

I am trying to create an array of multiple structs, I have created a protocol to do so that allows me to get and set similar properties but not when properties are different. My current code right now is below:

structs:

struct A: Pr {
poll: String
res: Int
}

struct B: Pr {
poll:String
ont: String
}

Protocol:

protocol Pr {
   var poll:String { get set }
}

var type without initialization:

var field:[Pr]
Camille Basbous
  • 299
  • 5
  • 11
  • 34
  • 1
    What is the problem with your code? – Nick Kim Apr 07 '21 at 17:09
  • Say, you had an array of different types with different properties - what's next? How are you going to use it? – New Dev Apr 07 '21 at 17:13
  • @NickKim my code does not allow me to get or set individual props of the structs which are res and ont respectively – Camille Basbous Apr 07 '21 at 17:15
  • @NewDev i want to be able to get or set them on a for loop – Camille Basbous Apr 07 '21 at 17:16
  • i can only get and set poll in this case – Camille Basbous Apr 07 '21 at 17:16
  • It's unclear what's not working. Could you show how you'd like to use `field`? – Larme Apr 07 '21 at 17:19
  • @Larme its not that it isn't working its that I cant get and set the individual props which are res and ont, for instance var field:[Pr] =[A(poll:"h", res:3), B(poll:"c", ont:"o"),] if i perform a change like field[0].poll = "p" or field[1].poll = "p" i can do it but not field[0].res = 5 or field[1].ont = "z" – Camille Basbous Apr 07 '21 at 17:24
  • Swift is strongly-typed - i.e. at compile-time, the type has to be known. There are some dynamic capabilities in Swift. And you can always (though I wouldn't necessarily recommend unless you must) use `Any`. But again - show the code of how you'd use it. And what happens if some property doesn't exist. – New Dev Apr 07 '21 at 17:30
  • @NewDev I have been trying to replicate what i read here https://www.swiftbysundell.com/questions/array-with-mixed-types/ maybe you can give it a. look its just to be able to use individual properties like in javascript with swift – Camille Basbous Apr 07 '21 at 17:33
  • I guess you could type check it on the fly in runtime and force cast to that structure to access the properties if you were keen on going through that array. But I would recommend against it – Nick Kim Apr 07 '21 at 17:35
  • This might be a good time to use an array of Tuple's `[(String, Int),(String, String)]()` https://stackoverflow.com/questions/24233084/how-do-i-add-a-tuple-to-a-swift-array – xTwisteDx Apr 07 '21 at 17:39

2 Answers2

3

After sanitizing your code a bit:

protocol Pr {
   var poll:String { get set }
}

struct A: Pr {
  var poll: String
  var res: Int
}

struct B: Pr {
  var poll:String
  var ont: String
}

var field: [Pr]

If I understand what you want is something like this:

// Just an example to fill array with something for a demo:
let a = A(poll: "pollA", res: 1)
let b = B(poll: "pollB", ont: "ont")

field = [Pr]()
field.append(a)
field.append(b)

// The actual solution
for pr in field {

    print(pr.poll)

    switch pr {
    case let pr as A:
        print(pr.res)
    case let pr as B:
        print(pr.ont)
    default:
        print("no more properties")
    }
}

That is: you can cast each item in array as given type to get properties specific to the type. This solves the GET problem. Set is more complicated, since you have an array and structs, which is copied when modified. So technically you can change them, but it won't be the same instance. The most simple way to resolve this, is to have a function that returns an array of modified [Pr] objects, which you can assign back to field:

func modify() -> [Pr] {

    var result = [Pr]()

    for pr in field {

        switch pr {
        case let pr as A:
            let a = A(poll: pr.poll + "changed", res: pr.res + 1)
            result.append(a)
        case let pr as B:
            let b = B(poll: pr.poll + "changed", ont: pr.ont + "changed")
            result.append(b)
        default:
            print("skip")
        }
    }

    return result
}

field = modify()

But this may not be a good solution if you are dealing with tons of data, lots of copies, etc, etc... So a more specific use case would be needed to make the answer more useful.

timbre timbre
  • 12,648
  • 10
  • 46
  • 77
  • This is the closest to a solution but as you said it isn't a good solution with tons of data and dynamic changes, thumbs up thought – Camille Basbous Apr 07 '21 at 18:56
1

You have two problems:

  1. An array of structs that conform to the Pr protocol is not an array of A and B objects:

  2. The Array and Struct types are both value types, so you can't cast an entry from your array to a specific concrete type without making a copy.

This code solves problem 1, but not problem 2:

(field[0] as? A)?.res = 5

I'm not sure if there is a solution to your problem, other than converting your A and B to class (reference type) objects. This code works:

protocol Pr {
   var poll:String { get set }
}

class A: Pr, CustomStringConvertible {
    var poll: String
    var res: Int
    
    init(poll: String, res: Int) {
        self.poll = poll
        self.res = res
    }
    
    var description: String {
        return ("A(poll: \(poll), res:\(res))")
    }
}

class B: Pr, CustomStringConvertible {
    var poll:String
    var ont: String

    init(poll: String, ont: String) {
        self.poll = poll
        self.ont = ont
    }
    var description: String {
        return ("B(poll: \(poll), ont:\(ont))")
    }
}

var field:[Pr] = [A(poll:"h", res:3), B(poll:"c", ont:"o"),]
field[0].poll = "p"
field[1].poll = "q"

(field[0] as? A)?.res = 5

(field[1] as? B)?.ont = "z"
Duncan C
  • 128,072
  • 22
  • 173
  • 272