0

I want to store values hierarchically, so that I can easily access them in dot notation:

my.node = "n"
my.node.a = "a1"
my.node.b = "b1"
my.node.b.x = "x2"

So basically when writing my.node without the dot, I want to get a string, but when writing my.node.a it should return an array/class/struct/.., that has the property a.

The structure will be defined once, so it could be a little messy or complex, but it will be used extensively throughout the whole app, so I want to avoid tons of array brackets or similar ugly constructs.

Is it possible?

sigma
  • 117
  • 7
  • You can make node a struct. And so on. – Arbitur Mar 05 '15 at 14:18
  • Yeah, but can it return a String when I just use the struct without accessing a member? Like: my.node returns "n"? If yes, could you provide a simple example please? – sigma Mar 05 '15 at 14:21

2 Answers2

2
my.node.a = "a1"
my.node.b = "b1"

The problem is that here you are proposing to use dot-notation. This is a built-in piece of Swift syntax; you don't get to redefine it. Thus you cannot talk like this unless my is an instance of a class with an instance property node, and node is an instance of a class with instance properties a and b. And ex hypothesi they aren't.

In other words, you can certainly do what you want to do (construct a hierarchy of nodes), but you can't fetch their values using the syntax you are describing. This is not Ruby (or similar) where you get to interfere in the message-sending mechanism.

So, my first cut at the sort of hierarchical structure you describe would look like this (and you'll see at once that I'm using exactly the sort of notation you would prefer not to use!):

class Thing {
    var val : String
    var d = [String:Thing]()
    init(_ val:String) {self.val = val}
    subscript(key:String) -> Thing {
        return self.d[key]!
    }
}

let my = Thing("n")
let a = Thing("a1")
let b = Thing("b1")
let x = Thing("x2")

b.d["x"] = x
my.d["a"] = a
my.d["b"] = b

let result1 = my.val
let result2 = my["a"].val
let result3 = my["b"].val
let result4 = my["b"]["x"].val

What I can do for you is eliminate the double subscripting of ["b"]["x"]. To do that, we need to reinvent key paths. (I say "reinvent" because NSObject already has this.) Thus, if we define Thing like this:

class Thing {
    var val : String
    var d = [String:Thing]()
    init(_ val:String) {self.val = val}
    subscript(key:String) -> Thing {
        let path = split(key, {$0=="."}, maxSplit: 1)
        if path.count == 2 {
            return self.d[path[0]]![path[1]]
        }
        return self.d[key]!
    }
}

Now we can write the last fetch like this:

let result4 = my["b.x"].val

You can readily see that if this were a chain of five or ten nodes, that at least would constitute a considerable notational savings:

let my = Thing("n")
let b = Thing("b1")
let x = Thing("x2")
let y = Thing("y3")
let z = Thing("z4")

y.d["z"] = z
x.d["y"] = y
b.d["x"] = x
my.d["b"] = b

let result = my["b.x.y.z"].val // better than nothing...

Okay - but having done that, I can eliminate the unwanted .val at the end of every fetch, and thus accomplish something like what you originally asked to accomplish!

class Thing {
    var val : String
    var d = [String:Thing]()
    init(_ val:String) {self.val = val}
    subscript(key:String) -> String {
        let path = split(key, {$0=="."}, maxSplit: 1)
        if path.count == 2 {
            return self.d[path[0]]![path[1]]
        }
        return self.d[key]!.val
    }
}

let my = Thing("n")
let b = Thing("b1")
let x = Thing("x2")
let y = Thing("y3")
let z = Thing("z4")

y.d["z"] = z
x.d["y"] = y
b.d["x"] = x
my.d["b"] = b

let result1 = my["b"]
let result2 = my["b.x.y.z"]

Your original question, to be sure, was about setting, not getting. You could do something similar with the subscript setter, but I leave this to you to work out the details, as the original question was not formulated in a way that allows me understand what the exact spec would be.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Thanks for the explanation, it is close to what we tried to achieve, but one problem: let result = my["b.x.y.z"].val will let you introduce typos in the string. So our idea was to create one big object my (or api) no matter how (assignment complexity doesn't matter) and have all the different values there as real members, like my.b.x.y.z. But we couldn't set a string value for both my.b and my.b.x, that is what the question is about. – sigma Mar 06 '15 at 15:21
  • That's a noble aim, @sigma, but what I'm saying is that you can't do that. I explain why, right at the start of my answer. You can do this in Ruby because you can send an arbitrary message to an arbitrary object (duck-typing) and deal with it when it arrives, but you can't do that in Swift, whose philosophy of message-sending is the opposite (strict typing). – matt Mar 06 '15 at 15:37
  • Yeah, we'll have to mix up then the best approximation. Thank you for your time! – sigma Mar 06 '15 at 16:34
0

Since it is not possible to overload the default assignment operator (https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html) and implicit type conversions are also disallowed in Swift(Does Swift support implicit conversion?), I'm afraid that you can't get exactly the behaviour you want. One solution could look like that:

struct Test : StringLiteralConvertible, Printable
{
    private var name = ""
    var value : Int = 0

    init(name n:String)
    {
        name = n
    }

    init(stringLiteral value: StringLiteralType)
    {
        self.init(name: value)
    }

    init(extendedGraphemeClusterLiteral value: String)
    {
        self.init(name: value)
    }

    init(unicodeScalarLiteral value: String)
    {
        self.init(name: value)
    }

    var description: String
    {
        get { return name }
    }
}

With value obviously of any type you want. It still requires .description to get the String though.

Community
  • 1
  • 1
Krzak
  • 1,441
  • 11
  • 12
  • It's not really about getting different values in, but different values out. Anyway, the Printable looks interesting, thank you! – sigma Mar 06 '15 at 16:36