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

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.

Related

Split a string on all characters except some with a regular expression

I have to split a long string with lyrics to a song into lines and then, for each line, split them into words. I'm going to hold this information in a 2 dimensional array.
I've seen some similar questions and they have been solved using [NSRegularExpression] (https://developer.apple.com/documentation/foundation/nsregularexpression)
but I can't seem to find any regular expression that equals "everything except something" which is what I want to split on when splitting a string into words.
More specifically I want to split on Everything except alphanumerics or ' or -. In Java this regular expression is [^\\w'-]+
Below is the string, followed by my Swift code to attempt to achieve this task (I just split on whitespace instead of actually splitting on words with "[^\w'-]+" as I can't figure out how to do it.
1 Is this the real life?
2 Is this just fantasy?
3 Caught in a landslide,
4 No escape from reality.
5
6 Open your eyes,
7 Look up to the skies and see,
8 I'm just a poor boy, I need no sympathy,
9 Because I'm easy come, easy go,
10 Little high, little low,
11 Any way the wind blows doesn't really matter to me, to me.
12
13 Mama, just killed a man,
(etc.)
let lines = s?.components(separatedBy: "\n")
var all_words = [[String]]()
for i in 0..<lines!.count {
let words = lines![i].components(separatedBy: " ")
let new_words = words.filter {$0 != ""}
all_words.append(new_words)
}
I suggest to use a reverse pattern, [\w'-]+, to match the strings you need and use the matches matching function.
Your code will look like:
for i in 0..<lines!.count {
let new_words = matches(for: "[\\w'-]+", in: lines![i])
all_words.append(new_words)
}
The following line of code:
print(matches(for: "[\\w'-]+", in: "11 Any way the wind blows doesn't really matter to me, to me."))
yields ["11", "Any", "way", "the", "wind", "blows", "doesn\'t", "really", "matter", "to", "me", "to", "me"].
One simple solution is to replace the sequences with a special character first and then split on that character:
let words = string
.replacingOccurrences(of: "[^\\w'-]+", with: "|", options: .regularExpression)
.split(separator: "|")
print(words)
However, if you can, use the system function to enumerate words.

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.

How to remove spaces from a string in Swift?

I have the need to remove leading and trailing spaces around a punctuation character.
For example: Hello , World ... I 'm a newbie iOS Developer.
And I'd like to have: > Hello, World... I'm a newbie iOS Developer.
How can I do this? I tried to get components of the string and enumerate it by sentences. But that is not what I need
Rob's answer is great, but you can trim it down quite a lot by taking advantage of the \p{Po} regular expression class. Getting rid of the spaces around punctuation then becomes a single regular expression replace:
import Foundation
let input = "Hello , World ... I 'm a newbie iOS Developer."
let result = input.replacingOccurrences(of: "\\s*(\\p{Po}\\s?)\\s*",
with: "$1",
options: [.regularExpression])
print(result) // "Hello, World... I'm a newbie iOS Developer."
Rob's answer also tries to trim leading/trailing spaces, but your input doesn't have any of those. If you do care about that you can just call result.trimmingCharacters(in: .whitespacesAndNewlines) on the result.
Here's an explanation for the regular expression. Removing the double-escapes it looks like
\s*(\p{Po}\s?)\s*
This is comprised of the following components:
\s* - Match zero or more whitespace characters (and throw them away)
(…) - Capturing group. Anything inside this group is preserved by the replacement (the $1 in the replacement refers to this group).
\p{Po} - Match a single character in the "Other_Punctuation" unicode category. This includes things like ., ', and …, but excludes things like ( or -.
\s? - Match a single optional whitespace character. This preserves the space after periods (or ellipses).
\s* - Once again, match zero or more whitespace characters (and throw them away). This is what turns your , World into , World.
For Swift 3 or 4 you can use :
let trimmedString = string.trimmingCharacters(in: .whitespaces)
This is a really wonderful problem and a shame that it isn't easier to do in Swift today (someday it will be, but not today).
I kind of hate this code, but I'm getting on a plane for 20 hours, and don't have time to make it nicer. This may at least get you started using NSMutableString. It'd be nice to work in String, and Swift hates regular expressions, so this is kind of hideous, but at least it's a start.
import Foundation
let input = "Hello, World ... I 'm a newbie iOS Developer."
let adjustments = [
(pattern: "\\s*(\\.\\.\\.|\\.|,)\\s*", replacement: "$1 "), // elipsis or period or comma has trailing space
(pattern: "\\s*'\\s*", replacement: "'"), // apostrophe has no extra space
(pattern: "^\\s+|\\s+$", replacement: ""), // remove leading or trailing space
]
let mutableString = NSMutableString(string: input)
for (pattern, replacement) in adjustments {
let re = try! NSRegularExpression(pattern: pattern)
re.replaceMatches(in: mutableString,
options: [],
range: NSRange(location: 0, length: mutableString.length),
withTemplate: replacement)
}
mutableString // "Hello, World... I'm a newbie iOS Developer."
Regular expressions can be very confusing when you first encounter them. A few hints at reading these:
The specific language Foundation uses is described by ICU.
Backslash (\) means "the next character is special" for a regex. But inside a Swift string, backslash means "the next character is special" of the string. So you have to double them all.
\s means "a whitespace character"
\s* means "zero or more whitespace characters"
\s+ means "one or more whitespace characters"
$1 means "the thing we matched in parentheses"
| means "or"
^ means "start of string"
$ means "end of string"
. means "any character" so to mean "an actual dot" you have to type "\\." in a Swift string.
Notice that I check for both "..." and "." in the same regular expression. You kind of have to do something like that, or else the "." will match three times inside the "...". Another approach would be to first replace "..." with "…" (the single ellipsis character, typed on a Mac by pressing Opt-;). Then "…" is a one-character punctuation. (You could also decide to re-expand all ellipsis back to dot-dot-dot at the end of the process.)
Something like this is probably how I'd do it in real life, get it done and shipped, but it may be worth the pain/practice to try to build this as a character-by-character state machine, walking one character at a time, and keeping track of your current state.
You can try something like
string.replacingOccurrences(of: " ,", with: ",") for every punctuation...
Interesting problem; here's my stab at a non-Regex approach:
func correct(input: String) -> String {
typealias Correction = (punctuation: String, replacement: String)
let corrections: [Correction] = [
(punctuation: "...", replacement: "... "),
(punctuation: "'", replacement: "'"),
(punctuation: ",", replacement: ", "),
]
var transformed = input
for correction in corrections {
transformed = transformed
.components(separatedBy: correction.punctuation)
.map({ $0.trimmingCharacters(in: .whitespaces) })
.joined(separator: correction.replacement)
}
return transformed
}
let testInput = "Hello , World ... I 'm a newbie iOS Developer."
let testOutput = correct(input: testInput)
// Hello, World... I'm a newbie iOS Developer.
If you were doing this manually by processing characters arrays, you would merely need to check the previous and next characters around spaces. You can achieve the same result using functional style programming with zip, filter and map:
let testInput = "Hello , World ... I 'm a newbie iOS Developer."
let punctuation = Set(".\',")
let previousNext = zip( [" "] + testInput, String(testInput.dropFirst()) + [" "] )
let filteredChars = zip(Array(previousNext),testInput)
.filter{ $1 != " "
|| !($0.0 != " " && punctuation.contains($0.1))
}
let filteredInput = String(filteredChars.map{$1})
print(testInput) // Hello , World ... I 'm a newbie iOS Developer.
print(filteredInput) // Hello, World... I'm a newbie iOS Developer.
Swift 4, 4.2 and 5
let str = " Akbar Code "
let trimmedString = str.trimmingCharacters(in: .whitespaces)

what does this dictionary iteration mean in 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,

Returning the string between the 5th and 6th Spaces in a String

I have a column of strings that look like this:
Target Host: dcmxxxxxxc032.erc.nam.fm.com Target Name:
dxxxxxxgsc047.erc.nam.fm.com Filesystem /u01 has 4.98% available space
- fallen below warning (20) or critical (5) threshold.
The column name is [Description]
The substring I would like returned is (dxxxxxxgsc047.erc.nam.fm.com)
The only consistency in this data is that the desired string occurs between the 5th and 6th occurrences of spaces " " in the string, and after the phrase "Target Name: " The length of the substring varies, but it always ends in another " ", hence my attempt to grab the substring between the 5th and 6th spaces.
I have tried
MID([Description],((FIND([Description],"Target Name: "))+13),FIND([Description]," ",((FIND([Description],"Target Name"))+14)))
But that does not work.
(Edit: We use Tableau 8.2, the Tableau 9 only functions can't be part of the solution, thanks though!)
Thank you in advance for your help.
In Tableau 9 you can use regular expressions in formulas, it makes the task simpler:
REGEXP_EXTRACT([Description], "Target Name: (.*?) ")
Alternatively in Tableau 9 you can use the new FINDNTH function:
MID(
[Description],
FINDNTH([Description]," ", 5) + 1,
FINDNTH([Description]," ", 6) - FINDNTH([Description]," ", 5) - 1
)
Prior to Tableau 9 you'd have to use string manipulation methods similar to what you've tried, just need to be very careful with arithmetic and providing the right arguments (the third argument in MID is length, not index of the end character, so we need to subtract the index of the start character):
MID(
[Description]
, FIND([Description], "Target Name:") + 13
, FIND([Description], " ", FIND([Description], "Target Name:") + 15)
- (FIND([Description], "Target Name:") + 13)
)
Well, you need to find "Target name: " and then the " " after it, not so hard. I'll split in 3 fields just to be more clear (you can mix everything in a single field). BTW, you were in the right direction, but the last field on MID() should be the string length, not the char position
[start]:
FIND([Description],"Target name: ")+13
[end]:
FIND([Description]," ",[start])
And finally what you need:
MID([Description],[start]+1,[end]-[start]-1)
This should do. If you want to pursue the 5th and 6th " " approach, I would recommend you to find each of the " " until the 6th.
[1st]:
FIND([Description], " ")
[2nd]:
FIND([Description], " ",[1st] + 1)
And so on. Then:
MID([Description],[5th]+1,[6th]-[5th]-1)
A simple solution -
SPLIT( [Description], " ", 3 )
This returns a substring from the Description string, using the space delimiter character to divide the string into a sequence of tokens.
The string is interpreted as an alternating sequence of delimiters and
tokens. So for the string abc-defgh-i-jkl, where the delimiter
character is ‘-‘, the tokens are abc, defgh, i and jlk. Think of these
as tokens 1 through 4. SPLIT returns the token corresponding to the
token number. When the token number is positive, tokens are counted
starting from the left end of the string; when the token number is
negative, tokens are counted starting from the right. -
Tableau String Functions
I don't know Tableau, but perhaps something like this?
MID(
MID([Description], FIND([Description],"Target Name: ") + 13, 50),
1,
FIND(MID([Description], FIND([Description],"Target Name: ") + 13, 50), " ")
)