Beginner Question: Func return String and Array - arrays

I'm experimenting manipulating basic Swift data types and tried to add a (basic) layer of complexity to a simple function examples I was practising from Swifts Documentation. I was able to return as a simple array but when I tried to return an array within a String I got errors. I've read all the Swift Documentation for Arrays to try and solve this first with no luck.
Here is my code for the successful Array return:
func namesList(person: String) -> [(String)] {
let register = ["RoboCop", person, "Terminator"]
return register.sorted()
}
... and my unsuccessful code:
func namesList(person: String) -> [(String)] {
let register = "The alphabetic order of names are \(["RoboCop", person, "Terminator"])"
return register.sorted()
}
I think the problem is in my return parameters but couldn't find a way to Return string and array?
Many thanks

.sorted() operates on Sequence, and both Array and String are Sequences. [String].sorted (your first example) return a [String]. String.sorted (your second example) returns a [Character], since a String is made up of Characters.
Your second example is not "an array nested in a String." It's just a String. \(...) performs string interpolation. It doesn't nest anything.
If you want to return the String, you'll need to return String.
func namesList(person: String) -> String {
let register = ["RoboCop", person, "Terminator"].sorted()
return "The alphabetic order of names are \(register)"
}
As a very minor point, [(String)] is unusual syntax and can be confusing to experienced Swift devs. The correct way to write this is just [String] with no extra parentheses.

register variable is actually a String in the second case.
You should change the return type to just string.

Related

How to use an array of functions in Swift

I have read all the posts I can find here about arrays of functions - great you can do it. I figured. But none of the posts show practically how to use them (at least not what I'm trying to do). Here's what I want - they can all take the same args, but that's not a requirement.
This article is close, and will allow me to loop through to execute each function (which meets the first goal).
https://stackoverflow.com/a/24447484/11114752
But... what if I want to execute a single function by reference?
In other words, how to call just the referenced Arity2 function - for example:
// None of these work (with or without the parameter labels)
funcs.Arity2(n: 2, S: "Fred) // value of type [MyFuncs] has no member .Arity2
funcs[Arity2](n: 2, S: "Fred") // no exact matches to call in subscript
funcs[.Arity2](n: 2, S: "Fred") // Cannot call value of non-function type...
let fn = funcs.first(where: { a whole ton of permutations here to try to match Arity2 }) -- a whole lotta frustrating nope...
Help, please! Nothing I've tried works. The pre-compiler just goes in circles making suggestions that don't pan out and it will not compile.
EDIT:
The reason for the array in the first place is that I'm going to have a quite a few functions, and I don't know what they all are in advance. Essentially, I want a plugin type of architecture. Where I can add to the list of functions (ideally within an extension of the class, but that's another problem..) and not change the processing loop that executes each function in order.
I assume you need something like
_ = funcs.first {
if case let MyFuncs.Arity2(f) = $0 {
f(2, "Fred")
return true
}
return false
}
It can be achieved in a much simpler way if you know the position of the function in the array.
Assuming you have:
func someFunc(n: Int, s: String) {
print("call \(n) \(s)")
}
var funcs = [MyFuncs.Arity2(someFunc)]
you can do:
if case .Arity2(let f) = funcs.first {
f(2, "Fred")
}
By replacing funcs.first with funcs[i] you can access the i-th index (first make sure it does exist).

Passing Swift strings as C strings in variable argument lists

I am trying to use the old printf-style string formatters to define a new Swift string from an old Swift string.
I notice this works fine as long as I am starting with a Swift string literal, but not a string variable.
// OK!
String(format:"%s", "hello world".cStringUsingEncoding(NSUTF8StringEncoding))
// ERROR: argument type '[CChar]?' does not conform to expected type 'CVarArgType'
let s = "hello world"
String(format:"%s", s.cStringUsingEncoding(NSUTF8StringEncoding))
Why does this happen?
And what's the best workaround?
(Please note that I am aware of but do not want to use the Cocoa string formatter %#. I want the printf-style formatting code because what I'm actually trying to do is get quick and dirty tabular alignment with codes like %-10s.)
This question concerns Swift 2.2.
Don't create a C string just for alignment. There is a method stringByPaddingToLength for that.
// Swift 2
s.stringByPaddingToLength(10, withString: " ", startingAtIndex: 0)
// Swift 3
s.padding(toLength: 10, withPad: " ", startingAt: 0)
Note that it will truncate the string if it is longer than 10 UTF-16 code units.
The problem here is, there are two methods in Swift named cStringUsingEncoding:
func cStringUsingEncoding(encoding: UInt) -> UnsafePointer<Int8>, as a method of NSString
func cStringUsingEncoding(encoding: NSStringEncoding) -> [CChar]?, as an extension of String
If we want a pointer, we need to ensure we are using an NSString, not a String.
In the first example, a string literal can be a String or NSString, so the compiler chooses NSString since the other one won't work.
But in the second example, the type of s is already set to String, so the method that returns [CChar]? is chosen.
This could be worked-around by forcing s to be an NSString:
let s: NSString = "hello world"
String(format:"%s", s.cStringUsingEncoding(NSUTF8StringEncoding))
kennytm's answer is clearly the best way.
But for anyone else who is still wondering about how to do it the wrong way, and get access to a c string based on a Swift String without going through NSString, this also seems to work (Swift 2.2):
var s:String = "dynamic string"
s.nulTerminatedUTF8.withUnsafeBufferPointer { (buffptr) -> String in
let retval:String = String(format:"%s",buffptr.baseAddress)
return retval
}

Efficient way to split string into an array and trim each value - Swift

In the quest to better understand swift, I'd like to see if there is a more efficient way to write the code below. The code should take the self.categories String, convert it into an array (values separated by commas), and trim each value, before returning the array.
func get_categories() -> Array<String>{
let categories = self.categories!.componentsSeparatedByString(",")
var categories_to_return = Array<String>()
for category in categories {
categories_to_return.append(category.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()))
}
return categories_to_return
}
I've got a suspicion I'm doing something wrong here - perhaps I don't need to create two separate arrays? Perhaps there is another shortcut I've not seen before? Many thanks in advance!
You could use map directly on the result of componentsSeparatedByString, like this, and return it without using another variable:
func get_categories() -> Array<String>{
return self.categories!.componentsSeparatedByString(",").map { $0.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) }
}
Note: in the map closure, $0 represents the current item from the array of components. It could also be written like this:
func get_categories() -> Array<String>{
return self.categories!
.componentsSeparatedByString(",")
.map { word in word.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) }
}
Here is a more generic function for general use cases.
func getSplitAndTrimmed(text: String) -> Array<String> {
return text.componentsSeparatedByString(",").map { $0.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) }
}
The functionality is the same as Eric's wrote.

Swift nil coalescing when using array contents

I'm just wondering if there's a more readable/less verbose way to write the following:
let rankMap: Dictionary<Int,String> = [1:"Ace", 11:"Jack", 12:"Queen", 13:"King"]
func getCardImage(suit: String, rank: Int) -> NSImage {
var imgRank: String
if rankMap[rank] == nil {
imgRank = rank.description
} else {
imgRank = (rankMap[rank]! as NSString).substringToIndex(1)
}
let imgSuit = (suit as NSString).substringToIndex(1)
//let imgRank: String = rankMap[rank] ?? rank.description
return NSImage(named: imgRank + imgSuit)
}
In particular, I'm interested in the part where rankMap is checked vs nil. Whatever is returned from the array needs to have every character except the first chopped off. I'm unable to write something like this:
let imgRank: String = (rankMap[rank] as NSString).substringToIndex(1) ?? rank.description
..due to the semantics around nil coalescing. Is there anything else I can do aside from the "hard way" of just doing an "if nil" check?
I removed the NSImage part, just so it's easier to test as a filename, but this should behave similarly.
func cardImage(suit: String, rank: Int) -> String {
let imgRankFull = rankMap[rank] ?? rank.description
let imgRank = first(imgRankFull) ?? " "
let imgSuit = first(suit) ?? " "
return imgRank + imgSuit
}
This shows how to use ??, but it still has lots of little problems IMO. I would recommend replacing suit and rank with enums. The current scheme has lots of ways it can lead to an error, and you don't do any good error checking. I've dodged by returning " ", but really these should be either fatalError or an optional, or a Result (an object that carries a value or an error).
You don't need substringToIndex to get the first character of a Swift string. String is a CollectionType, to which this generic function from the standard library applies:
func first<C : CollectionType>(x: C) -> C.Generator.Element?
So you might start by trying something like this:
let imgRank = first(rankMap[rank]) ?? rank.description // error
That doesn't work, for two reasons:
String.Generator.Element is a Character, not a String, so if you're going to put it in a ?? expression, the right operand of ?? needs to also be a Character. You can construct a Character from a String (with Character(rank.description)), but that'll crash if that string has more than one character in it.
rankMap[rank] is an optional, so you can't pass it directly to first. But if you try to force-unwrap it immediately (with first(rankMap[rank]!), you'll crash upon looking up something not in the dictionary.
You could try solving these problems together by putting the ?? inside the first call, but you probably want a two-character string for rank 10.
It'd be nice if we could treat String like an Array, which provides var first for getting its first element. (And have a version of first for strings that returns a String.) Then we could take advantage of optional chaining to put a whole expression to the left of the ??, and just write:
let imgRank = rankMap[rank]?.first ?? rank.description
Well, let's write it in an extension!
extension String {
var first : String? {
// we're overloading the name of the global first() function,
// so access it with its module name
switch Swift.first(self) {
case let .Some(char):
return String(char)
case .None:
return nil
}
}
}
Now you can be nice and concise about it:
let imgRank = rankMap[rank]?.first ?? String(rank)
(Notice I'm also changing rank.description to String(rank). It's good to be clear -- description gives you a string representation of a value that's appropriate for printing in the debugger; String() converts a value to a string. The former is not guaranteed to always match the latter, so use the latter if that's what you really want.)
Anyhow, #RobNapier's advice stands: you're probably better off using enums for these. That gets you a lot less uncertainty about optionals and invalid lookups. But seeing what you can do with the standard library is always a useful exercise.

Swift: optional array count

In Objective-C, if I had the following property:
#property (strong, nonatomic) NSArray * myArray;
A method to return a number of objects in myArray would look like:
- (NSInteger) numberOfObjectsInMyArray
{
return [self.myArray count];
}
This would return either the number of objects in the array, or 0 if myArray == nil;
The best equivalent I can think of for doing this in Swift is:
var myArray: Array<String>?
func numberOfObjectsInMyArray() -> Int
{
return myArray ? myArray!.count : 0
}
So checking the optional array contains a value, and if so unwrap the array and return that value, otherwise return 0.
Is this the correct way to do this? Or is there something simpler?
Try using the nil coalescing operator.
According to the Apple Documentation:
The nil coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil.
So your function could look like this:
func numberOfObjectsInMyArray() -> Int {
return (myArray?.count ?? 0)
}
I agree with others that this could be a bad idea for a number of reasons (like making it look like there is an array with a count of "0" when there isn't actually an array at all) but hey, even bad ideas need an implementation.
EDIT:
So I'm adding this because two minutes after I posted this answer, I came across a reason for doing exactly what the author wants to do.
I am implementing the NSOutlineViewDataSource protocol in Swift. One of the functions required by the protocol is:
optional func outlineView(_ outlineView: NSOutlineView,
numberOfChildrenOfItem item: AnyObject?) -> Int
That function requires that you return the number of children of the item parameter. In my code, if the item has any children, they will be stored in an array, var children: [Person]?
I don't initialize that array until I actually add a child to the array.
In other words, at the time that I am providing data to the NSOutlineView, children could be nil or it could be populated, or it could have once been populated but subsequently had all objects removed from it, in which case it won't be nil but it's count will be 0. NSOutlineView doesn't care if children is nil - all it wants to know is how many rows it will need to display the item's children.
So, it makes perfect sense in this situation to return 0 if children is nil. The only reason for calling the function is to determine how many rows NSOutlineView will need. It doesn't care whether the answer is 0 because children is nil or because it is empty.
return (children?.count ?? 0) will do what I need. If children is nil it will return 0. Otherwise it will return count. Perfect!
That looks like the simpler way.
The Objective-C code is shorter only because nil is also a form of 0, being a C-based language.
Since swift is strongly typed you don't have such a shorthand. In this specific case it requires a little more effort, but in general it saves you most of the headaches caused by loose typing.
Concerning the specific case, is there a reason for making the array optional in the first place? You could just have an empty array. Something like this might work for you:
var myArray: Array<String> = []
func numberOfObjectsInMyArray() -> Int {
return myArray.count
}
(Source for this information)
How about using optional for return value?
var myArray: Array<String>?
func numberOfObjectsInMyArray() -> Int? {
return myArray?.count
}
I think that this way is safer.
(Source for this information)

Resources