what does this dictionary iteration mean in swift? - swift

A dictionary and an iteration operation in swfit
let namesAndScores = ["Anna": 2, "Brian": 2, "Craig": 8, "Donna"; 6]
for key in namesAndScores.keys {
print("\(key), ", terminator: "")
}
print("")
the result is Brian, Anna, Craig, Donna,
what is this operation doing? and why does "terminator: " not appear in the result?

Q: what is this operation doing?
The following line is a dictionary of type [String : Int]
let namesAndScores = ["Anna": 2, "Brian": 2, "Craig": 8, "Donna": 6]
Next, take note of the following line in your loop:
namesAndScores.keys
This .keys property gives us a new array composed of just the keys from your dictionary. For example:
let keys = ["Anna", "Brian", "Craig", "Donna"]
Next, you're simply looping through the array I depicted above:
for key in namesAndScores.keys {
print("\(key), ", terminator: "")
}
Q: and why does "terminator: " not appear in the result?
Terminator is just a parameter that lets you set the print line terminator. In other words, it lets you chose what gets printed after each line. By default its "\n" which gives us a new line.
In your example you have: print("\(key), ", terminator: ""). With your print statement's terminator set to "". You're simply telling your print() function to print without a trailing newline. Which prints your values like this:
Brian, Anna, Craig, Donna,
However, you could pass any string into your terminator parameter for example:
print("\(key), ", terminator: " -hi- ")
Which would give you this:
Brian, -hi- Anna, -hi- Craig, -hi- Donna, -hi-
So, when you just use the common print("\(key), ") without setting the terminator, your terminator parameter gets a default value which is equivalent to:
print("\(key), ", terminator: "\n")
This prints each item on a new line, like so:
Brian,
Anna,
Craig,
Donna,
update based on comment:
Q: Why is Brian before Anna in the result while Brian is after
Anna in the Dictionary?
Dictionaries are unsorted. So, the resulting array from .keys will be in an unpredictable order. Fortunately, you are able to sort the resulting array in place by simply adding .sort(<) in your for-loop.
for key in namesAndScores.keys.sort(<) {
print("\(key), ", terminator: "")
}
prints:
Anna, Brian, Craig, Donna,

Related

I wonder what is different between separator and terminator in printing?

I am wondering about this printing thing. You could notice this code concept different between this code concept.
print(1, 2, 3, 4, 5, separator: "...")
//1...2...3...4...5
and
for n in 1...5 {
print(n, separator: " ", terminator: "...")
}
// 1...2...3...4...5...
I thought separator supposed be space for add text each of the items but terminators for default the newlines as known \n which you could see newlines sign if you were using the swift playground as well. If you put that three periods on the separator could not display that '...' on loops but printing can. I get a bit confuse about this code. so Let me know about this code. Might I was wrong at those things. I was learning about Swift.
As documented for the print(_:separator:terminator:) method:
separator: A string to print between each item.
terminator: The string to print after all items have been printed.
This is per call to print. So the separator is only useful when you provide more than one value to print.
print(arg1, arg2, ..., argN, separator: someSeparator, terminator: someTerminator)
This results in:
arg1 someSeparator arg2 someSeparator ... someSeparator argN someTerminator
(but without the spaces which I added for clarity).
When you only print one value, such as in the code in your question, the separator isn't used at all so you are only seeing the terminator at the end of each value printed.
Code such as:
print(1, 2, 3, 4, 5, separator: " ", terminator: "...")
will give the results you seem to be looking for:
1 2 3 4 5...
separator is the text to print between the vales given to print.
terminator is the text to print after all values were printed. so:
print(val1, val2, val3, ..., separator: "<SEP>", terminator: "<TERM>")
will print
val1<SEP>val2<SEP>val3<SEP>...<TERM>
if only one value is given to print, then it will NOT print the separator, because it has nothing to separate! (you don't separate one value from itself). but it will print the terminator given (or the default terminator, if one is not supplied).
therefor,
print(1, 2, 3, 4, 5, separator: "...")
//1...2...3...4...5
will print the values 1, 2, 3, 4, 5 separated by "..." and terminated by the default terminator,
and
for n in 1...5 {
print(n, separator: " ", terminator: "...")
}
// 1...2...3...4...5...
will print every number from 1 to 5, with the text ... after every number, and
will not print the separator, because the print is given only one value to print.

What does backslash do in Swift?

In the following line of code, what is the backslash telling Swift to do?
print("The total cost of my meal is \(dictionary["pizza"]! + dictionary["ice cream"]!)")
The backslash has a few different meanings in Swift, depending on the context. In your case, it means string interpolation:
print("The total cost of my meal is \(dictionary["pizza"]! + dictionary["ice cream"]!)")
...is the same as:
print("The total cost of my meal is " + String(dictionary["pizza"]! + dictionary["ice cream"]!))
But the first form is more readable. Another example:
print("Hello \(person.firstName). You are \(person.age) years old")
Which may print something like Hello John. You are 42 years old. Much clearer than:
print("Hello " + person.firstName + ". You are " + String(person.age) + " years old")
That's called String interpolation. When you want to embed the value of a variable in a String, you have to put the variable name between parentheses and escape the opening parentheses with a backslash. This way the compiler knows that it has to substitute the value of the variable there instead of using the String literal of the variable name.
For more information on the topic, have a look at the String interpolation part of the Swift Language Guide.

How to print an array of characters in swift without a line terminator

This should be simple to solve. When I try printing an array of characters in a Swift playground, I see each character printed with a end of line terminator...
For Example when I type this in a Swift Playground.
var strTokenizeMe = "Go Pro"
for chrInStr in strTokenizeMe { print(chrInStr,"*")}
This prints
G
o
P
r
o
Now I do NOT want and end of line terminator, so I add terminator: " " at the end of the print statement, like this ...
for chrInStr in strTokenizeMe { print(chrInStr, terminator: " ")}
But when I do this NOTHING gets printed.
In a Playground you need to print one final newline, otherwise the output
is not flushed to the output window:
for chrInStr in strTokenizeMe { print(chrInStr, terminator: " ")}
print()
A (not necessarily better) alternative would be to concatenate
the characters before printing them:
print(strTokenizeMe.map(String.init).joined(separator: " "))
The problem does not occur when running a compiled program, because
the standard output is always flushed on program exit.

Bug in replacingOccurrences()?

I wonder if the String function replacingOccurrences(of: String, with: String) has a bug:
let s = "Hello World!"
let cleaned : String = s.replacingOccurrences(of: " ", with: " ")
print("cleaned = '\(cleaned)'")
I want to replace multi spaces into one
" " to " "
but the string remain the same. I've done that hundreds of times in obj-c, so is this a bug in Swift?
It's not a bug. You are replacing every occurrence of 2 spaces with one space. The method does not work recursively, so the 5 spaces are reduced to 3.
You could use regular expression, it replaces all occurrences of one or more spaces with one space:
let s = "Hello World!"
let cleaned = s.replacingOccurrences(of: " +", with: " ", options: .regularExpression)
print("cleaned = '\(cleaned)'")
As mentioned by Vadian, this is not a bug, this is how your code should work.
And here is another way to get the same result:
let cleaned = s.components(separatedBy: .whitespaces).filter({ !$0.isEmpty }).joined(separator: " ")
You first separate the string by whitespace, then exclude all whitespaces using filter, then join the words of the string separating them by whitespaces.

The expression prints itself in unexpected order

When I print a log information like this:
val idList = getIdList
log info s"\n\n-------idList: ${idList foreach println}"
It shows me:
1
2
3
4
5
-------idList: ()
That makes sense because foreach returns Unit. But why does it print the list of id first? idList is already evaluated in the previous line (if that's the cause)!
And how to make it print it in expected order - after idList:?
This is because you're not evaluating the log string to read what you want, you evaluate it to:
\n\n -------idList: ()
However, the members of the list appear in the output stream as a side effect, due to the println call in the string interpolation.
EDIT: since clarification was requested by the OP, what happens is that the output comes from two sources:
${idList foreach println} evaluates to (), since println itself doesn't return anything.
However, you can see the elements printed out, because when the string interpolation is evaluated, println is being called. And println prints all the elements into the output stream.
In other words:
//line with log.info() reached, starts evaluating string before method call
1 //println from foreach
2 //println from foreach
3 //println from foreach
4 //println from foreach
5 //println from foreach
//string argument log.info() evaluated from interpolation
-------idList: () //log info prints the resultant string
To solve your problem, modify the expression in the interpolated string to actually return the correct string, e.g.:
log info s"\n\n-------idList: ${idList.mkString("\n")}"
Interpolation works in a following way:
evaluate all arguments
substitute their results into resulting string
println is a Unit function that prints to the standard output, you should use mkstring instead that returns a string
log info s"\n\n-------idList: ${idList.mkString("(", ", ", ")")}"
As pointed out by #TheTerribleSwiftTomato , you need to give an expression that returns a value and has no other side-effect. So simply do it like this:
val idList = getIdList
log info s"\n\n-------idList: ${idList mkString " "}"
For example, this works for me:
val idList = List(1, 2, 3, 4, 5)
println(s"\n\n-------idList: ${idList mkString " "}")
Output:
-------idList: 1 2 3 4 5