swift: working with string - swift

We have a simple string:
let str = "\"abc\", \"def\",\"ghi\" , 123.4, 567, \"qwe,rty\""
If we do this:
let parsedCSV = str
.components(separatedBy: .newlines)
.filter { !$0.isEmpty }
.map { $0.components(separatedBy: ",") }
.map { $0.map { $0.trimmingCharacters(in: .whitespaces) } }
print(parsedCSV)
we get this:
[["\"abc\"", "\"def\"", "\"ghi\"", "123.4", "567", "\"qwe", "rty\""]]
Is there a simple solution (using functional programming) not to split the last element \"qwe,rty\", because we know that it's one whole thing?

Well this is a hack, it works for this case.... not very simple solution for complex issue ...
let str = "\"abc\", \"def\",\"ghi\" , 123.4, 567, \"qwe,rty\""
let parsedCSV = str
.components(separatedBy: .newlines)
.filter { !$0.isEmpty }
.map { $0.components(separatedBy: ",") }
.map { $0.map { $0.trimmingCharacters(in: .whitespaces) } }.reduce([]) { (result, items) -> [String] in
var goodItems = items.filter{ $0.components(separatedBy: "\"").count == 3 || $0.components(separatedBy: "\"").count == 1}
let arr = items.filter{ $0.components(separatedBy: "\"").count == 2}
var join:[String] = []
for x in 0..<arr.count {
let j = x + 1
if j < arr.count {
join = [arr[x] + "," + arr[j]]
}
}
goodItems.append(contentsOf: join)
return goodItems
}
print(parsedCSV)
print out
["\"abc\"", "\"def\"", "\"ghi\"", "123.4", "567", "\"qwe,rty\""]

I've done that.
let str = "_, * ,, \"abc\", 000, def, ghi , 123.4,, 567, \"qwe,rty,eur\", jkl"
let separator = ","
let parsedCSV = str
.components(separatedBy: .newlines)
.filter { !$0.isEmpty }
.map { $0.components(separatedBy: separator).map { $0.trimmingCharacters(in: .whitespaces) } }
.reduce([]) { (result, items) -> [String] in
var result: [String] = []
for item in items {
guard let last = result.last, last.components(separatedBy: "\"").count % 2 == 0 else {
result.append(item)
continue
}
result.removeLast()
let lastModified = record + separator + item
result.append(lastModified)
}
return result
}.map { $0.trimmingCharacters(in: CharacterSet(charactersIn: "\"")) }
print(parsedCSV)
["_", "*", "", "abc", "000", "def", "ghi", "123.4", "", "567",
"qwe,rty,eur", "jkl"]

If you do not mind using Regular Expression, you can write something like this:
let str = "_, * ,, \"abc\", 000, def, ghi , 123.4,, 567, \"qwe,rty,eur\", jkl"
let pattern = "((?:[^\",]|\"[^\"]*\")*)(?:,|$)"
let regex = try! NSRegularExpression(pattern: pattern)
let parsedCSV = regex.matches(in: str, options: .anchored, range: NSRange(0..<str.utf16.count))
.map {$0.range(at: 1)}
.filter {$0.location != NSNotFound && $0.location < str.utf16.count}
.map {String(str[Range<String.Index>($0, in: str)!]).trimmingCharacters(in: .whitespaces)}
.map {$0.trimmingCharacters(in: CharacterSet(charactersIn: "\""))}
print(parsedCSV) //->["_", "*", "", "abc", "000", "def", "ghi", "123.4", "", "567", "qwe,rty,eur", "jkl"]

Related

swift string separation but include the

I need to separate a string to a array of substring but need to include ",.?!" as the substring in Swift.
var sentence = "What is your name?" into
var words = ["What", "is", "your", "name", "?"]
I know I can use this to separate the white space, but I need the ".,?!" to be separated into a word in the words array. How can I do that?
var words = sentence.components(separatedBy: " ")
I only get ["What", "is", "your", "name?"]
I need to separate the ? at the end word, and make words array like this:
var words = ["What", "is", "your", "name", "?"]
You can enumerate your substrings in range using .byWords options, append the substring to your words array, get the substring range upperBound and the enclosed range upperBound, remove the white spaces on the resulting substring and append it to the words array:
import Foundation
let sentence = "What is your name?"
var words: [String] = []
sentence.enumerateSubstrings(in: sentence.startIndex..., options: .byWords) { substring, range, enclosedRange, _ in
words.append(substring!)
let start = range.upperBound
let end = enclosedRange.upperBound
words += sentence[start..<end]
.split{$0.isWhitespace}
.map(String.init)
}
print(words) // "["What", "is", "your", "name", "?"]\n"
You can also use a regular expression to replace the punctuation by the same punctuation preceded by a space before splitting your words by whitespaces:
let sentence = "What is your name?"
let words = sentence
.replacingOccurrences(of: "[.,;:?!]",
with: " $0",
options: .regularExpression)
.split{$0.isWhitespace}
print(words) // "["What", "is", "your", "name", "?"]\n"
Swift native approach:
var sentence = "What is your name?"
for index in sentence
.indices
.filter({ sentence[$0].isPunctuation })
.reversed() {
sentence.insert(" ", at: index)
}
let words = sentence.split { $0.isWhitespace }
words.forEach { print($0) }
This will print:
What
is
your
name
?
This function will split on whitespace and also include each punctuation character as a separate string. Apostrophes are treated as part of a word, so "can't" and "it's" are kept together as a single string. This function will also handle double spaces and tabs.
func splitSentence(sentence: String) -> [String] {
var result : [String] = []
var word = ""
let si = sentence.startIndex
for i in 0..<sentence.count {
let c = sentence[sentence.index(si, offsetBy: i)]
if c.isWhitespace {
if word.count > 0 {
result.append(word)
word = ""
}
} else if (c.isLetter || (String(c) == "'")) {
word = word + String(c)
} else {
if word.count > 0 {
result.append(word)
word = ""
}
result.append(String(c))
}
}
if word.count > 0 {
result.append(word)
}
return result
}
Here is some testing code:
func test(_ sentence: String, _ answer: [String]) {
print("--------------------------------")
print("sentence=" + sentence)
let result : [String] = splitSentence(sentence: sentence)
for s in result {
print("s={" + s + "}")
}
if answer.count != result.count {
print("#### Answer count mismatch")
}
for i in 0..<answer.count {
if answer[i] != result[i] {
print("### Mismatch: {" + answer[i] + "} != {" + result[i] + "}")
}
}
}
func runTests() {
test("", [])
test(" ", [])
test(" ", [])
test(" a", ["a"])
test("a ", ["a"])
test(" a", ["a"])
test(" a ", ["a"])
test("a ", ["a"])
test("aa", ["aa"])
test("a a", ["a", "a"])
test("?", ["?"])
test("a?", ["a", "?"])
test("???", ["?", "?", "?"])
test("What is your name?", [ "What", "is", "your", "name", "?" ])
test("What is your name? ", [ "What", "is", "your", "name", "?" ])
test("La niña es linda.", [ "La", "niña", "es", "linda", "."])
test("ñññ ñ ññ ñ", [ "ñññ", "ñ", "ññ", "ñ" ])
test("It's the 'best'.", [ "It's", "the", "'best'", "." ])
test("¿Cómo te llamas?", [ "¿", "Cómo", "te", "llamas", "?" ])
test("你好吗?", [ "你好吗", "?" ])
}
XCTAssertEqual(
"¿What is your name? My name is 🐱, and I am a cat!"
.split(separator: " ")
.flatMap { $0.split(includingSeparators: \.isPunctuation) }
.map(Array.init)
.map { String($0) },
[ "¿", "What", "is", "your", "name", "?",
"My", "name", "is", "🐱", ",", "and", "I", "am", "a", "cat", "!"
]
)
public enum Spliteration<Element> {
case separator(Element)
case subSequence([Element])
}
public extension Array {
init(_ spliteration: Spliteration<Element>) {
switch spliteration {
case .separator(let separator):
self = [separator]
case .subSequence(let array):
self = array
}
}
}
public extension Sequence {
/// The first element of the sequence.
/// - Note: `nil` if the sequence is empty.
var first: Element? {
var iterator = makeIterator()
return iterator.next()
}
func split(includingSeparators getIsSeparator: #escaping (Element) -> Bool)
-> AnySequence< Spliteration<Element> > {
var separatorFromPrefixIteration: Element?
func process(next: Element?) -> Void {
separatorFromPrefixIteration =
next.map(getIsSeparator) == true
? next
: nil
}
process(next: first)
let prefixIterator = AnyIterator(
dropFirst(
separatorFromPrefixIteration == nil
? 0
: 1
),
processNext: process
)
return .init {
if let separator = separatorFromPrefixIteration {
separatorFromPrefixIteration = nil
return .separator(separator)
}
return Optional(
prefixIterator.prefix { !getIsSeparator($0) },
nilWhen: \.isEmpty
).map(Spliteration.subSequence)
}
}
}
public extension AnyIterator {
/// Use when `AnyIterator` is required / `UnfoldSequence` can't be used.
init<State>(
state: State,
_ getNext: #escaping (inout State) -> Element?
) {
var state = state
self.init { getNext(&state) }
}
/// Process iterations with a closure.
/// - Parameters:
/// - processNext: Executes with every iteration.
init<Sequence: Swift.Sequence>(
_ sequence: Sequence,
processNext: #escaping (Element?) -> Void
) where Sequence.Element == Element {
self.init( state: sequence.makeIterator() ) { iterator -> Element? in
let next = iterator.next()
processNext(next)
return next
}
}
}
public extension AnySequence {
/// Use when `AnySequence` is required / `AnyIterator` can't be used.
/// - Parameter getNext: Executed as the `next` method of this sequence's iterator.
init(_ getNext: #escaping () -> Element?) {
self.init( Iterator(getNext) )
}
}
public extension Optional {
/// Wraps a value in an optional, based on a condition.
/// - Parameters:
/// - wrapped: A non-optional value.
/// - getIsNil: The condition that will result in `nil`.
init(
_ wrapped: Wrapped,
nilWhen getIsNil: (Wrapped) throws -> Bool
) rethrows {
self = try getIsNil(wrapped) ? nil : wrapped
}
}
Not answering using swift, but I believe the algorithm can be emulated with any language.
Done the implementation using Java. The code uses standard Java libraries and not external ones.
private void setSpecialCharsAsLastArrayItem() {
String name = "?what is your name?";
String regexCompilation = "[$&+,:;=?##|]";
Pattern regex = Pattern.compile(regexCompilation);
Matcher matcher = regex.matcher(name);
StringBuilder regexStr = new StringBuilder();
while (matcher.find()) {
regexStr.append(matcher.group());
}
String stringOfSpecialChars = regexStr.toString();
String stringWithoutSpecialChars = name.replaceAll(regexCompilation, "");
String finalString = stringWithoutSpecialChars + " "+stringOfSpecialChars;
String[] splitString = finalString.split(" ");
System.out.println(Arrays.toString(splitString));
}
will print [what, is, your, name, ??]
Here is the solution (Swift 5):
let sentence = "What is your name?".replacingOccurrences(of: "?", with: " ?")
let words = sentence.split(separator: " ")
print(words)
Output:
["What", "is", "your", "name", "?"]

How can I expand a Dict(string, array) in Swift 5

Can I expand the dictionary to an array of pairs in Swift 5 using map/reduce or will I have to do a for each ?
let dict = ["A": ["1","2","3","4"],
"B": ["5","6","7","8"]]
???
//result = [["A", "1"],["A", "2"],....["B", "5"],....]
let result = dict.map { (letter: String, digits: [String]) in
return digits.map { digit in
return [letter, digit]
}
}.reduce([]) {
$0 + $1
}
This one is shortest solution.
let result = dict.map { dic in
dic.value.map { [dic.key : $0] }
}.reduce([], +)
Here's a solution that doesn't need reduce:
let dict = ["A": ["1","2","3","4"],
"B": ["5","6","7","8"]]
let result = dict.map { kv in kv.value.map { [kv.key, $0] } }.flatMap { $0 }

Anagrams in Swift with wildcard (or joker)

Based on this function in Swift:
func anagrams(word: String, from words: [String]) -> [String] {
let anagrammedWord = word as NSString
let length = anagrammedWord.length
var aDic = [unichar:Int]()
for i in 0..<length {
let c = anagrammedWord.character(at: i)
aDic[c] = (aDic[c] ?? 0) + 1
}
let foundWords = words.filter {
let string = $0 as NSString
guard length == string.length else { return false }
var bDic = [unichar:Int]()
for i in 0..<length {
let c = string.character(at: i)
let count = (bDic[c] ?? 0) + 1
if count > aDic[c] ?? 0 {
return false
}
bDic[c] = count
}
return true
}
return foundWords
}
I'm trying to get this working with wildcard, but it doesn't.
How can I check with ? or _ ?
Edit:
This is how it work :
input : anagrams(word: "MILK", from: ["EGGS", "MILK", "LINK", "LIMK", "TREE"])
output : ["MILK", "LIMK"]
I would like to work with wildcards, for example :
input : anagrams(word: "?ILK", from: ["EGGS", "MILK", "LINK", "LIMK", "TREE"])
output : ["MILK", "LINK", "LIMK"]
You could use this:
func anagrams(word: String, from words: [String]) -> [String] {
let characterCount = word.count
let histogramOfWord = histogram(of: word)
let foundWords = words.filter { otherWord in
//Exit early if the character counts differ
if otherWord.count != characterCount {
return false
}
let h2 = histogram(of: otherWord)
//If they have similar histograms, count that word in
if h2 == histogramOfWord {
return true
}
//Compare the histograms with wildcards taken into consideration
else {
guard let wildCards = histogramOfWord["?"], wildCards > 0 else {
return false
}
let numberOfDifferences: Int = h2.map { entry in
let key = entry.key
let value1 = histogramOfWord[key] ?? 0
let value2 = entry.value
let difference = value2 - value1
return difference >= 0 ? difference : 0
}.reduce(0, +)
return numberOfDifferences == wildCards
}
}
return foundWords
}
It calls this function that calculates the frequencies of each character in a string:
func histogram(of string: String) -> [Character:Int] {
var dictionary: [Character:Int] = [Character:Int]()
for i in 0..<string.count {
let c = string[string.index(string.startIndex, offsetBy: i)]
dictionary[c] = (dictionary[c] ?? 0) + 1
}
return dictionary
}
And you could use it like so:
print(anagrams(word: "MILK", from: ["EGGS", "MILK", "LINK", "LIMK", "TREE"]))
//["MILK", "LIMK"]
print(anagrams(word: "?ILK", from: ["EGGS", "MILK", "LINK", "LIMK", "TREE"]))
//["MILK", "LINK", "LIMK"]
print(anagrams(word: "TREE?", from: ["ARETE", "BERET", "BARTE", "MILKA"]))
//["ARETE", "BERET"]
print(anagrams(word: "TREE", from: ["ATRE", "CRET", "LINK", "RETE", "TREE"]))
//["RETE", "TREE"]

Check for duplicate characters in string using for-loops in swift

I did this using while loops but I'm wondering if there's a way to do this with for loops. I'm trying to write this clean so I can write it on a whiteboard for people to understand.
var str = "Have a nice day"
func unique(_ str: String) -> String {
var firstIndex = str.startIndex
while (firstIndex != str.endIndex) {
var secondIndex = str.index(after: firstIndex)
while (secondIndex != str.endIndex) {
if (str[firstIndex] == str[secondIndex]) {
return "Not all characters are unique"
}
secondIndex = str.index(after: secondIndex)
}
firstIndex = str.index(after: firstIndex)
}
return "All the characters are unique"
}
print("\(unique(str))")
You can use the indices of the characters:
var str = "Have a nice day"
func unique(_ str: String) -> String {
for firstIndex in str.characters.indices {
for secondIndex in str.characters.indices.suffix(from: str.index(after: firstIndex)) {
if (str[firstIndex] == str[secondIndex]) {
return "Not all characters are unique"
}
}
}
return "All the characters are unique"
}
print("\(unique(str))")
I used a hash to do it. Not sure how fast it is but it doesn't need to be in my case. (In my case I'm doing phone numbers so I get rid of the dashes first)
let theLetters = t.components(separatedBy: "-")
let justChars = theLetters.joined()
var charsHash = [Character:Int]()
justChars.forEach { charsHash[$0] = 1 }
if charsHash.count < 2 { return false }
... or more compact, as an extension...
extension String {
var isMonotonous: Bool {
var hash = [Character:Int]()
self.forEach { hash[$0] = 1 }
return hash.count < 2
}
}
let a = "asdfasf".isMonotonous // false
let b = "aaaaaaa".isMonotonous // true
Here is the for-loop version of your question.
let string = "Have a nice day"
func unique(_ string: String) -> String {
for i in 0..<string.characters.count {
for j in (i+1)..<string.characters.count {
let firstIndex = string.index(string.startIndex, offsetBy: i)
let secondIndex = string.index(string.startIndex, offsetBy: j)
if (string[firstIndex] == string[secondIndex]) {
return "Not all characters are unique"
}
}
}
return "All the characters are unique"
}
There are a lot of ways this can be achieved and this is just one way of doing it.
As #adev said, there are many ways to finish this. For example, you can use only one for-loop with a dictionary to check the string is unique or not:
Time complexity: O(n). Required O(n) additional storage space for the dictionary.
func unique(_ input: String) -> Bool {
var dict: [Character: Int] = [:]
for (index, char) in input.enumerated() {
if dict[char] != nil { return false }
dict[char] = index
}
return true
}
unique("Have a nice day") // Return false
unique("Have A_nicE-dⒶy") // Return true
let str = "Hello I m sowftware developer"
var dict : [Character : Int] = [:]
let newstr = str.replacingOccurrences(of: " ", with: "")
print(newstr.utf16.count)
for i in newstr {
if dict[i] == nil {
dict[i] = 1
}else{
dict[i]! += 1
}
}
print(dict) // ["e": 5, "v": 1, "d": 1, "H": 1, "f": 1, "w": 2, "s": 1, "I": 1, "m": 1, "o": 3, "l": 3, "a": 1, "r": 2, "p": 1, "t": 1]
You can find any value of char how many times write in string object.
this is my solution
func hasDups(_ input: String) -> Bool {
for c in input {
if (input.firstIndex(of: c) != input.lastIndex(of: c)) {
return true
}
}
return false
}
let input = "ssaajaaan"
var count = 1
for i in 0..<input.count
{
let first = input[input.index(input.startIndex, offsetBy: i)]
if i + 1 < input.count
{
let next = input[input.index(input.startIndex, offsetBy: i + 1)]
if first == next
{
count += 1
}
else
{
count = 1
}
}
if count >= 2
{
print(first," = ",count)
}
}
let inputStr = "sakkett"
var tempStr = String()
for char in inputStr
{
if tempStr.contains(char) == false
{
tempStr = tempStr.appending(String(char))
let filterArr = inputStr.filter({ $0 == char})
if filterArr.count > 1 {
print("Duplicate char is \(char)")
}
}
}
//Output:
Duplicate char is k
Duplicate char is t

How can I nest debug output in Swift?

Suppose I have an object like:
class MyClass
{
let a_number : Int?
let a_string : String?
let an_array_of_strings : Array<String>?
let an_array_of_objs : Array<Any>?
}
How could I make it so that when I print this object to console, z is indented like so:
MyClass
a_number = 4
a_string = "hello"
an_array_of_strings = ["str1",
"str2",
"str3"]
an_array_of_objs = [MyClass
a_number = 5
a_string = "world"
an_array_of_strings = nil
an_array_of_objs = nil]
I'd do that with a recursive function with an accumulator parameter for the indentation. It defaults to no indentation and is increased by the first column's width on each recursive call:
func describe<T>(_ x: T, indent: String = "") -> String
{
let mirror = Mirror(reflecting: x)
guard !mirror.children.isEmpty else { return x is String ? "\"\(x)\"" : "\(x)" }
switch mirror.displayStyle! {
case .tuple:
let descriptions = mirror.children.map { describe(unwrap($0.value), indent: indent) }
return "(" + descriptions.joined(separator: ",\n\(indent)") + ")"
case .collection:
let descriptions = mirror.children.map { describe(unwrap($0.value), indent: indent) }
return "[" + descriptions.joined(separator: ",\n\(indent)") + "]"
case .dictionary:
let descriptions = mirror.children.map { (child: Mirror.Child) -> String in
let entryMirrors = Array(Mirror(reflecting: unwrap(child.value)).children)
return describe(unwrap(entryMirrors[0].value), indent: indent) + ": "
+ describe(unwrap(entryMirrors[1].value))
}
return "[" + descriptions.joined(separator: ",\n\(indent)") + "]"
case .set:
let descriptions = mirror.children.map { describe(unwrap($0.value), indent: indent) }
return "Set(" + descriptions.joined(separator: ",\n\(indent)") + ")"
default:
let childrenWithLabel = mirror.children.filter { $0.label != nil }
let separator = " = "
let firstColumnWidth = (childrenWithLabel.map { Int($0.label!.characters.count) }.max() ?? 0)
+ separator.characters.count
let subindent = indent + String(repeating: " ", count: firstColumnWidth)
let lines = childrenWithLabel.map {
indent
+ ($0.label! + separator).padding(toLength: firstColumnWidth, withPad: " ", startingAt: 0)
+ describe(unwrap($0.value), indent: subindent)
}
return (["\(mirror.subjectType)"] + lines).joined(separator: "\n")
}
}
This function uses the unwrap(_:) function from my answer to another question
func unwrap<T>(_ any: T) -> Any
{
let mirror = Mirror(reflecting: any)
guard mirror.displayStyle == .optional, let first = mirror.children.first else {
return any
}
return first.value
}
When using describe(_:) like this (I made MyClass a struct so I can use the memberwise initializer):
struct MyClass: CustomStringConvertible
{
let a_number : Int?
let a_string : String?
let an_array_of_strings : Array<String>?
let an_array_of_objs : Array<Any>?
var description: String { return describe(self) }
}
print(MyClass(a_number: 4, a_string: "hello",
an_array_of_strings: ["str1", "str2", "str3"],
an_array_of_objs: [
MyClass(a_number: 5, a_string: "world",
an_array_of_strings: nil, an_array_of_objs: nil)]))
then the output is
MyClass
a_number = 4
a_string = "hello"
an_array_of_strings = ["str1",
"str2",
"str3"]
an_array_of_objs = [MyClass
a_number = 5
a_string = "world"
an_array_of_strings = nil
an_array_of_objs = nil]
Please note that this is only tested with your specific example and some simple additions. I am also not happy about the forced unwrap of mirror.displayStyle but in my shallow testing this only ever happened when mirror.children is empty, which is covered by the preceding guard. If anybody has investigated this more closely, I'd love a comment. I haven't found anything in the documentation of Mirror.
And just like in my answer to your related question I mixed up where the = is supposed to be. Just the other way round this time, duh! :)