31
\$\begingroup\$

What are some tips for code-golfing in Swift? Its focus on safety seems to make it difficult to golf in, but that makes little tips and even more useful. Are there any features in Swift that may help it excel in code-golf in certain applications?

Please post one tip per answer.

\$\endgroup\$
3
  • 12
    \$\begingroup\$ You can count on us--if it's meant to be safe, it will be made d̶a̶n̶g̶e̶r̶o̶u̶s̶ golfy! \$\endgroup\$ Commented May 3, 2016 at 1:21
  • 10
    \$\begingroup\$ Hopefully some good tips come in swiftly. \$\endgroup\$
    – DJMcMayhem
    Commented May 3, 2016 at 1:27
  • 6
    \$\begingroup\$ Swift golfing? I thought golf was supposed to be a slow-paced, calm game... \$\endgroup\$
    – Jojodmo
    Commented May 3, 2016 at 4:29

15 Answers 15

8
\$\begingroup\$

Getting Rid of Argument Labels

Argument labels eat up a lot of characters:

String(repeating:"-",count:81)
g.joined(separator:"")

But function types in Swift have no argument labels, so if we use the function type instead of the initializer/method directly, we don't need argument labels. The shortest way I know is to put the initializer/method in a closure, then call the closure, and immediately call the resulting function:

{String.init}()("-",81)
{g.joined}()("")

This works for overloaded methods as well, as long as the compiler can infer the method to call from the parameter types.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ In case anybody is curious, argument labels come from Objective-C, which borrowed the from Smalltalk. In those languages, the function "name" and the first label were combined. \$\endgroup\$
    – Eric Xue
    Commented Jul 22, 2022 at 0:19
7
\$\begingroup\$

Ranges

One thing that is really helpful is creating ranges using the ... or ..< operators

For example

array[0..<n] //first n elements of array
array[k..<n] //kth through nth elements of array
array[k...n] //kth through n-1 elements of array

So, to get the 2nd through 4th values of an array

let myArray = ["ab", "cd", "ef", "gh", "ij", "kl", "mn", "op"]
print(myArray[1..<4]) //["cd", "ef", "gh"]

Practical Use

let a = [3, 1, 4, 1, 5, 9]

Using

for v in a[0...3]{print(v)}

Is 8 bytes shorter than

for n in 0...3{let v=a[n];print(v)}
\$\endgroup\$
2
  • 5
    \$\begingroup\$ …is for n in 0...3{print(a[n])} not valid? \$\endgroup\$ Commented May 11, 2017 at 21:00
  • 1
    \$\begingroup\$ You can also use unbound ranges like ...3 and 3.... For that example you can use a[...3].map{print($0)}. \$\endgroup\$
    – xAlien95
    Commented Jul 2, 2020 at 19:43
7
\$\begingroup\$

Closures:

The use of variables the hold a function vs. using a function itself can help:

65 bytes:

var r:(String,Int)->String={return String(repeating:$0,count:$1)}

66 bytes:

func r(s:String,i:Int)->String{return String(repeating:s,count:i)}

Small difference here, but it'll show more in some puzzles.

Shortening Functions:

Looking at the previous example reminds me of something. Sometimes, if you will be using a function enough times, it is worth the space to rename it:

This:

String(repeating:$0,count:$1)

To this:

var r:(String,Int)->String={return String(repeating:$0,count:$1)}

Or, actually, this is better:

var r=String.init(repeating:count:)

That way you just call r("Hello World",8) instead of String(repeating:"Hello World",count:8)

Leaving Out Type Declarations:

I once created a closure without setting the argument type, thus creating a shorter answer:

var f={(i)->Int in i-1+i%2*2}

The compiler inferred that i is in Int.

Create Arrays the Fast Way:

If you need an array of Ints, use a Range to create it:

Array(0...5)

This does the same thing as:

[0,1,2,3,4,5]

Arrays Instead of If or Switch:

Instead of doing this:

if n==0{return "a"}else if n==1{return "b"}else{return "c"}

You can probably do this:

return ["a","b","c"][n]

Shorten Types:

If you are using type conversion a lot, you might want to create a type alias:

typealias f=Float

Map:

Remember that you often don't need to use the return keyword in the map function.

Running Swift Online:

Although Try It Online does not support Swift It does now!

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Thanks for the post. Helped me a lot. tio.run/nexus now works with swift :) \$\endgroup\$
    – palme
    Commented Mar 8, 2019 at 15:59
4
\$\begingroup\$

Enumeration

You can chain forEach from enumerated() on a collection type to get a reference to the object (or value type) in a collection, as well as its index:

[1,2,3,4,5].enumerated().forEach{print($0,$1)}

or

for (c,i) in [1,2,3,4,5].enumerated(){print(c,i)}

or (even shorter CountableClosedRange syntax)

(1...5).enumerated().forEach{print($0,$1)}

Prints:

0 1
1 2
2 3
3 4
4 5
\$\endgroup\$
4
\$\begingroup\$

try

In Swift 2.x and above, functions that traditionally handled errors by passing a pointer to an NSError object as a function parameter now throw their error.

This means that this:

var regex = NSRegularExpression(pattern: "\"((http)s?://.*?)\"", options: nil, error: nil)

now looks like:

do {
    let regex = try NSRegularExpression(pattern: "\"((http)s?://.*?)\"", options: [])
} catch {
    print(error)
}

This can be shortened by using try? or try!. try? will evaluate the expression to nil if an error is thrown. try! will crash your program if an error is thrown, and should be only used in cases where there will never be an error thrown.

let regex = try! NSRegularExpression(pattern: "\"((http)s?://.*?)\"", options: [])

try? and try! save at least 13 bytes from the do-try-catch loop. Note that you also save at least one more byte by passing in an empty array ([]) for options instead of nil as well.

\$\endgroup\$
4
\$\begingroup\$

Reducing Arrays

Iterating with for-in loops through an array to get a single value such as the sum of the elements inside, or the product of its elements may be too long for how simple it actually is. You can just use the reduce() method. Some examples:

var array = [1,2,3,4,5,6,7]

Adding up the elements in an array with for-in loops:

var sum = 0

for item in array{
    sum += item
}
print(sum)

can be simplified to:

print(array.reduce(0, +))

And getting the product of the elements inside of the array with for-in loops:

var multiplier = 1
for item in array{
    multiplier *= item
}
print(multiplier)

can also be reduced to:

print(array.reduce(1, *))
\$\endgroup\$
2
  • \$\begingroup\$ Don't you lose more bytes by declaring the ** function than you would by just calling pow manually? \$\endgroup\$
    – JAL
    Commented Mar 25, 2017 at 0:47
  • \$\begingroup\$ @JAL yes, but if you call pow 10 times, it saves a small amount of bytes. Not really the best golfing technique, but it's just another potential help. It was not meant for pow specifically, but for other operations such as searching for prime numbers, if you do that 3 times, it saves a huge amount of bytes \$\endgroup\$
    – Mr. Xcoder
    Commented Mar 25, 2017 at 9:27
4
\$\begingroup\$

Import

You can replace import Foundation with import UIKit for 5 bytes shorter, as UIKit does import Foundation already.

\$\endgroup\$
0
3
\$\begingroup\$

Substring

Sometimes, you can save bytes by falling back onto Foundation types instead of using pure Swift types. Compare accessing a substring of an NSString vs a Swift String type:

let x:NSString = "hello world"
x.substringToIndex(5) // "hello"

let y = "hello world"
y.substringToIndex(y.startIndex.advancedBy(5)) // "hello"

Even with the 9 characters lost by declaring x as an NSString, you save 25 more by using the Foundation type, since substringToIndex takes an Int as a parameter for NSString, vs an Index struct (String.CharacterView.Index) for Swift String types.

I should note that the availability of Foundation types may differ across multiple platforms (OS X, Linux, etc). Most Foundation classes are NSUnimplemented in the open-source version of Swift.

\$\endgroup\$
3
\$\begingroup\$

.map()

Combining .map() with trailing closure syntax can tighten up for-loops. We can put the things we want to iterate over into an array, then use .map() to perform some action on each element.

For example, we can use .map() to write that old chestnut, Fizzbuzz, in one line.

var d: [Int] = Array(1...100)

d.map{$0%15 == 0 ? print("Fizzbuzz") : $0%3 == 0 ? print("Fizz") : $0%5 == 0 ? print("Buzz") : print($0)}

Outside of golf, .map() can help cut down on repetition. For example, suppose you have a view you need to position programmatically. You can put the anchors for the view into an anonymous array and run .map() over it to set each constraint's .isActive to true, like so:

_ = [
        view.topAnchor.constraint(equalTo: view.topAnchor, constant: 40),
        view.widthAnchor.constraint(equalTo: view.widthAnchor),
        view.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        view.bottomAnchor.constraint(equalTo: view.bottomAnchor)
    ].map { $0.isActive = true }
\$\endgroup\$
1
  • 2
    \$\begingroup\$ Isn't it better to use forEach in your second example? map should really be used for transforming the contents of an array, and not as an iteration shortcut. In this case, you're discarding the result. \$\endgroup\$
    – JAL
    Commented Aug 8, 2017 at 20:49
3
\$\begingroup\$

Golfing variable assignments in control flow structures using tuples

Consider you want to use a while loop, and you want to use the same thing in both the condition and the block to follow. Then, an inline assignment in a tuple would most likely help. The longer your attribute, the better! Consider this (3 bytes shorter):

func f(s:[Int]){var k=s,i=0;while(i=k.count,i>0).1{print(i,i+k[i-1]);k.removeLast();}}

over this:

func g(s:[Int]){var k=s,i=0;while k.count>0{i=k.count;print(i,i+k[i-1]);k.removeLast();}}

Notice the (i=k.count,i>0).1 part, which is quite interesting.


Inspired by one of Herman Lauenstein's answers.

\$\endgroup\$
3
\$\begingroup\$

Type Inference

Swift's type checker is very good at inferring types — a blessing like no other when golfing — but sometimes it needs a little help. Consider this code (borrowed from one of my own answers):

let function = { (decoy: String, target: String) in
  let array = ([decoy] + { [String].init }()(target, 511)).shuffled()
  // ...
}

(For context, this creates a 512-element array where all but one of the elements are identical.)

Thanks to closure tricks, this is already pretty golfy once whitespace is removed and names are shortened:

let f={(d:String,t:String)in let a=([d]+{[String].init}()(t,511)).shuffled()
/* ... */}

But there are three words that stick out like sore thumbs, and they all start with an S and end with a tring. Thankfully, we can get rid of all three by combining two more tricks.

Type Placeholders (Swift 5.6 and newer)

If the type checker can't infer an entire type on its own, but it can infer some of its type parameters (i.e. things like the T in SomeType<T>), then you can replace those parameters with an underscore to tell the type checker to figure them out for you. For instance, Array<String> can become Array<_> (as long as something else makes it clear that the type parameter is a String).

Of course, this also applies to the syntactic sugar versions of Array<Element>, Dictionary<Key, Value>, and Optional<Wrapped>, as well as tuple types, function types, and anything else that counts as a type. So [String] can be replaced with [_] in much the same manner.

Taking advantage of this, we can save 5 bytes in our example:

let f={(d:String,t:String)in let a=([d]+{[_].init}()(t,511)).shuffled()
/* ... */}

Now we've got the other two to deal with. We can't use the type placeholder trick with these, since the type checker will then struggle with just about everything. So we've got to hint at these types another way...

Appending Literals

Let's start with an absolutely horrible example that still illustrates the point well:

let p={(n:Int)in print(n)}

Let's say you want to get rid of the bulky Int type signature. You can't just outright remove it, because then the type checker won't have any way of knowing that n is an Int. So you need to give it some other, shorter way of figuring the type out.

One way to do this is by doing something with the value that doesn't affect how the program runs, but still makes it clear what the type is. In this instance, you can simply add 0 to n:

let p={n in print(n+0)}

Since we no longer have to declare any explicit types, we can do away with the closure header altogether and use implicit closure parameters instead:

let p={print($0+0)}

Note, however, that this only saves bytes if you only use each parameter once or twice, and that implicit parameters can't be used inside of other closures.

Here's a table of literals you can use in this manner:

Type Type Hint
Array $0+[]
Dictionary $0+[:]
String $0+""
Int $0+0
Double $0+0.0

Using these, our first example can be reduced to:

let f={d,t in let a=([d+""]+{[_].init}()(t,511)).shuffled()
/* ... */}

t can be inferred on its own without a problem here, so we don't need to append an empty literal to it.

With implicit closure parameters, we can make this even smaller:

let f={let a=([$0+""]+{[_].init}()($1,511)).shuffled()
/* ... */}
\$\endgroup\$
2
\$\begingroup\$

Swift's ternary operator is very terse: condition ? action : otheraction

If the condition is true, do one thing, if not, do something else.

textColor = bgIsBlack ? .white : .black

This makes the textColor white if the background is black, or black if the background is any other color.

The nil coalescing operator is even terser: a ?? b

Let's say you're checking JSON for a certain key so you can present the key's value as title text. If the key isn't present (i.e value is nil), we want to give the title default text.

title = keysValue ?? "Not Available"
\$\endgroup\$
2
\$\begingroup\$

Repeating Strings

Unfortunately, Swift does not support String multiplication with *, likewise Python. A good method you can use instead is String(repeating:count:), but unfortunately that's not really golfy. Compare these two approaches:

var a=String(repeating:"abc",count:3)

and

var a="";for _ in 0..<3{a+="abc"}

The second one is a couple of bytes shorter, but that cannot be used in a closure... Better yet, and it also works in closures:

(0..<3).map{_ in"abc"}.joined()

And what if I do it multiple times? Well, you can use String.init(). Now, this may save lots of bytes. For example (68 bytes):

let k=String.init(repeating:count:)
print(k("abcd",9)+k("XYZxyz",9))

instead of (74 bytes):

print(String(repeating:"abcd",count:9)+String(repeating:"XYZxyz",count:9))

or (70 bytes):

var f={String(repeating:$0,count:$1)}
print(f("abcd",9)+f("XYZxyz",9))

But make sure your String is long enough. If you are using String(repeating:"abc",3), it is much better to use "abcabcabc" instead.

\$\endgroup\$
1
  • \$\begingroup\$ +1 because I had no idea variables could be initializers like that. \$\endgroup\$
    – Daniel
    Commented Aug 9, 2017 at 19:49
1
\$\begingroup\$

Conversion to string

When possible, it is shorter to use string interpolation than to use the String method for converting to a string.

String(a)

vs

"\(a)"
\$\endgroup\$
0
\$\begingroup\$

Boolean Literals

Instead of using Boolean literals, which take up 4 or 5 bytes, use a 3-byte comparison instead.

Literal Comparison Bytes Saved
true 0<1 1
false 0>1 2
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.