0

I just started with Swift a few days ago and I am struggling with all the different Datatypes...

Lets say we do this:

var myarray = ["justin", "steve", "peter"]
var newString = myarray[2]

so why I cant now print just the "p" from "peter"?

print(newString[0])

---> gives me an error:

"'subscript' is unavailable: cannot subscript String with an Int"

in this topic: [Get nth character of a string in Swift programming language

it says: "Note that you can't ever use an index (or range) created from one string to another string"

But I cant imagine, that there isn't way to handle it...

Because When I do this:

var myarray = ["justin", "steve", "p.e.t.e.r"]
var newString = myarray[2]
let a : [String] = newString.componentsSeparatedByString(".")
print(a[2])
  • then it works. It prints (in this case) "t".

So how I can split it "SeparatedByString" 'Nothing'?

Im sure to solve this will help me also at many other problems. I hope I postet the question in the way it should be done.

Thank you for any solution or tips :)

Community
  • 1
  • 1
scopaest
  • 3
  • 2

1 Answers1

2

The instance method componentsSeparatedByString applied to a String instance yields an array of String instances, namely [String]. The elements of an array can be indexed in the classic Java/C++ fashion (let thirdElement = array[2]).

If you'd like to split your String instance ("peter") into an array of single character String instances, you can make use of the CharacterView property of a String instance, and thereafter map each single Character in the CharacterView back to a single-character String instance

let str = "peter"
let strArray = str.characters.map(String.init(_:)) // ["p", "e", "t", "e", "r"]
let thirdElement = strArray[2] // t

This is quite roundabout way though (String -> CharacterView -> [String] -> String), and you're most likely better off simply looking at the excellent answer (covering direct String indexing) in the dupe thread dug up by @Hamish:

The possible corner case where the above could be applicable, as pointed out by Hamish below, is if you very frequently need access to characters in the String at specific indices, and like to make use of the O(1) random access available to arrays (more so applicable if you are working with immutable String:s, such that the corresponding String array only needs to be generated once for each immutable String instance).

Community
  • 1
  • 1
dfrib
  • 70,367
  • 12
  • 127
  • 192
  • 1
    This is a good approach if you need to *frequently* access the characters at a given index though, without having to do a linear walk for each access :) – Hamish Feb 14 '17 at 20:38
  • Thank you. :) it gives me 2 Errors ("Expected ',' separator" "Expected expression in list of expressions") but it's maybe a Problem of my Swift version (2.1.1). I'll also check the linked thread. Thanks, helped me to not rage! :D – scopaest Feb 14 '17 at 20:40
  • @Hamish that's true! I don't really know what I _really_ think of the `.map(String.init(_:))` variation though, as compared to `.map { String($0) }`, but hey, why not :D – dfrib Feb 14 '17 at 20:41
  • @scopaest replace the 2nd line in the snippet above with `let strArray = str.characters.map { String($0) }` and it might work for Swift 2.1 (tested and working for Swift 2.2). – dfrib Feb 14 '17 at 20:43
  • @dfri Yeah, I usually go back to using a closure expression when it gets to the point where you have to disambiguate the `init` with `(_:)`, but I guess it's a matter of preference :) – Hamish Feb 14 '17 at 20:46
  • @Hamish I also prefer the closure for (imo) better semantics, but I'm uncertain if the closure approach could have a minimal (/negligible :) extra overhead as compared to directly supplying an initializer-function-reference. I've never benchmarked this, and the compiler _should_ optimize the former to the latter, but I can't really say. – dfrib Feb 14 '17 at 20:49
  • @dfri yea, it works! thank you! :D – scopaest Feb 14 '17 at 21:08
  • @dfri The cost difference should indeed be negligible. Without optimisations, I would say that the closure expression should actually be *slightly* faster. The reasoning for this is that `String.init(_:)` cannot be passed directly into `map(_:)` – it first has to be partially applied with the metatype `String.self`, which will create a new closure on the heap. The closure expression on the other hand is stored statically, so doesn't incur an allocation cost. – Hamish Feb 14 '17 at 21:21
  • But with optimisations, the compiler can perform all sorts of clever specialisations and inlinings, so it's a lot harder to reason with (but from a quick benchmark, it appears the closure expression still wins). – Hamish Feb 14 '17 at 21:21
  • @Hamish very instructive, thanks! My own speculation was too heavily C++ inspired, thinking to compare a lightweight function pointer to a slightly more complex function object (the closure); really good to know that this is not applicable in Swift. Btw, this should be no difference even in case `map` didn't have `@autoclosure`? – dfrib Feb 14 '17 at 21:38
  • @dfri You can indeed think of it like that, it's just the other way round – in this case, the closure expression is the (more or less) lightweight function pointer (although it may carry a heap-allocated context object around with it to store info about captured values, and may be partially applied itself to capture stuff). `String.init(_:)` on the other hand needs partial applying in this case, so a new closure (function object) is created on the fly (at least for unoptimised builds) to encapsulate the application of `(String.Type) -> (Character) -> String` to `(Character) -> String`. – Hamish Feb 14 '17 at 22:24
  • And `map` doesn't have `@autoclosure` ;) – Hamish Feb 14 '17 at 22:29
  • @Hamish I see, thanks for the expansive explanation! Ah yes, mixup `@autoclosure` vs final-argument as trailing closure on my behalf :) – dfrib Feb 14 '17 at 22:34
  • No problem :) Also if you had a global function with the signature `(Character) -> String` and passed that to `map(_:)` rather than `String.init`, that would also be comparable to passing a simple function pointer to `map(_:)`. Also now that I come to think of it, things get slightly more complex when passing functions to parameters where generics are involved – they are partially applied with a "reabstraction thunk helper" (I believe it's in order to unify the calling conventions of the functions passed). This will incur the cost of a closure allocation, but can be specialised in -O builds. – Hamish Feb 14 '17 at 22:38
  • Sorry for going on a bit – this is what happens when you go through the emitted SIL :P – Hamish Feb 14 '17 at 22:40
  • @Hamish no worries at all (the contrary), evey new comment from you here is much appreciated and instructive; I've dug quite deep in the stdlibs myself, but none at all into SIL (& emitted/generated SIL). The latter is apparently also very interesting, will need to look into this myself, inspired and prompted by your nice comments above :) so thanks again! – dfrib Feb 15 '17 at 07:15