Adding string to array - arrays

When i am parsing through the string from the url, I append each new line to an array. However I only want to add if the field is not empty. So if the column[5] is an empty string I don't append it to the array. For example I have two lines of strings:
1,2,3,4,5,
1,2,3,4,5,6
I only want to append when there are 6
However I am getting a index out of range error on the if column[5] == "" line
func readDataFromURL(url: String) -> String?{
if let url = URL(string: url)
{
do {
var contents = try String(contentsOf: url)
contents = contents.replacingOccurrences(of: "\r", with: "")
csv(data: contents)
return contents
} catch {
print("File error \(url)")
return nil
}
}
return nil
}
func csv(data: String) -> [[String]] {
var result: [[String]] = []
let rows = data.components(separatedBy: "\n")
for row in rows {
let columns = row.components(separatedBy: ",")
if columns[5] == "" {
continue
} else {
result.append(columns)
}
}
return result
}

Your current code will crash if num of elements is less than 6 , Replace
if columns[5] == "" {
continue
} else {
result.append(columns)
}
with
if columns.count > 5 && columns.last != "" {
result.append(columns)
}

Related

String Duplicate count

I am trying to prints counts of a duplicate from an input string through the use of hash map. I am not sure why solution is not working. I come from a python background
//Write an efficient program to print all the duplicates and their counts in the input string
func letters(inputs: String)->[Character:Int] {
var result = [Character:Int]()
for input in inputs {
if let value = result[input] {
return [input : value]
}
else {
result[input] = (result[input] ?? 0)+1
}
}
return [:]
}
letters(inputs:"geeksforgeeks")
["e": 1]
Try this:
func letters(inputs: String) -> [Character:Int] {
var result = [Character:Int]()
for input in inputs{
if let value = result[input] {
result[input] = value + 1
}
else{
result[input] = 1
}
}
return result
}

How to subtract each Int value based on some condition in an array by a constant in Swift?

I have an array ["11-16,20-23", "11-16,20-23", "11-16,20-23"]
How can I subtract Int value which is greater than 12 of this array in Swift?
So array becomes ["11-4,8-11", "11-4,8-11", "11-4,8-11"] like this
my current workaround is
func test() {
let hours = "11-16,20-23|11-16,20-23|11-16,20-23|11-16,20-23|13-17,20-24|11-16,20-23|11-16,20-23"
var firstHalf : [String] = []
var secondHalf : [String] = []
let split = hours.components(separatedBy: "|")
for i in 0..<split.count {
let index = split[i].components(separatedBy: ",")
firstHalf.append(index[0])
secondHalf.append(index[1])
}
print(firstHalf)
print(secondHalf)
let final = firstHalf.first?.components(separatedBy: "-")
print(final?[0])
print(final?[1])
}
It is possible to do this using .map. Firstly if we just start with your data in an array
let data = ["11-16,20-23", "11-16,20-23", "11-16,20-23"]
We need to perform the following steps.
Split each String in the array by ","
Split the substrings from the above result by the "-"
Check to see if the value is greater than 12, subtract 12 if needed
Join the convert the new values with a "-"
Join the result from above with a ","
When we use split on a String we get the following [String.SubSequence], which we need to convert back into a [String] we can do this by performing a .map { String($0) } immediately after performing the split.
let data = ["11-16,20-23", "11-16,20-23", "11-16,20-23"]
let result = data.map { $0
.split(separator: ",") // split each String item at the comma
.map { String($0) } // convert to String as we have subsequences
.map { $0
.split(separator: "-") // Now split each string by the dash
.map { String($0) } // convert to String
.map(convertTime) // use the above convert time function
.joined(separator: "-") // join the values with a dash
}
.joined(separator: ",") // join the values with a comma
}
func convertTime(_ hour: String) -> String {
// When casting to Int we get Int? so we need to unwrap it
if let value = Int(hour), value > 12 {
return String(value - 12)
} else {
return hour
}
}
print(result) // ["11-4,8-11", "11-4,8-11", "11-4,8-11"]
You could make it simpler by using .components(separatedBy:)
let result = data.map { $0
.components(separatedBy: ",")
.map { $0
.components(separatedBy: "-")
.map(convertTime)
.joined(separator: "-")
}
.joined(separator: ",")
}
Update
Mapping to days of the week is possible. There are a couple of things that we need. Firstly, we need to make sure that the order of the data matches the order of the days of the week. If they do not match, or are not consistently ordered in the same way, then it would not be possible to map them.
Secondly, from your comment it looks like you want map them to a custom struct. So we need to create the struct
struct OpeningTimes {
let day: String
let fromWorkingHours: String
}
Then we can use our answer from above which outputs the updated hours to result and we can zip it with an array of the days of the week. For ease I have just used the output from the above in the code below so that you have a contained example.
let result = ["11-4,8-11", "11-4,8-11", "11-4,8-11", "11-4,8-11", "11-4,8-11", "11-4,8-11", "11-4,8-11"]
let weekDays = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
let arrayOfOpeningTimes = zip(weekDays, result).map {
OpeningTimes(day: $0.0, fromWorkingHours: $0.1)
}
print(arrayOfOpeningTimes)
This will create an array of OpeningTimes, just make sure that you have the same number of items in each array of week days and hours, otherwise there will be missing values.
To avoid code that is hard to understand or to long I think it's best to create to functions to handle part of the logic
//Handle the inner pair of open/close hours
func openHours(from string: String.SubSequence) -> (Int, Int)? {
let values = string.split(separator: "-")
.compactMap { Int($0) }
.map { $0 > 12 ? $0 - 12 : $0 }
guard values.count == 2 else { return nil }
return (values[0], values[1])
}
//Handle am and pm opening hours string
func dayHours(from string: String.SubSequence) -> String {
return string.split(separator: ",")
.compactMap(openHours)
.map { "\($0.0)-\($0.1)"}
.joined(separator: ", ")
}
Then the actual conversion becomes quite simple
let output = hours.split(separator: "|")
.map(dayHours)
To return this using a 12h format we need to change the openHours function
func openHours(from string: String.SubSequence) -> (String, String)? {
let values = string.split(separator: "-")
.compactMap { Int($0) }
.map { $0 > 12 ? "\($0 - 12)pm" : "\($0)am" }
guard values.count == 2 else { return nil }
return (values[0], values[1])
}
func test() {
let hours = "11-16,20-23|11-4,6-5|11-16,20-23|11-16,20-23|13-17,20-24|11-16,20-23|11-16,20-23"
let split = hours.components(separatedBy: "|")
var finalarray = [String]()
for i in 0..<split.count {
let splitedArray = split[i].components(separatedBy: ",")
var array = [String]()
for item in splitedArray {
let tmpstr = item.split(separator: "-").compactMap {
Int($0)
}.map { "\($0 > 12 ? $0 - 12 : $0)"}.joined(separator: "-")
array.append(tmpstr)
}
let str = array.joined(separator: ",")
finalarray.append(str)
}
print(finalarray.joined(separator: "|"))
}

Create array of strings from a string that contains certain (dual) characters

I have a string of words and mathematical/programmatically used symbols. For example, something like this:
let source = "a + b + 3 == c"
(Note: you cannot rely on spaces)
I also have an array of strings that I need to filter out of the source string:
let symbols = ["+", "-", "==", "!="]
Now, I need to create an array of the matching items (with duplicates). In this case that would be ["+", "+", "=="].
From what I've tried, == is two characters, so I cannot do the following:
let source = "a + b + 3 == c"
let symbols = CharacterSet(charactersIn: "+-=≠") // not '==' and '!=', but '=' and '≠' due to it being a CharacterSet
let operations = source
.map { String($0) }
.filter { char in symbols.contains(UnicodeScalar(char)!) }
print(operations)
// Output: ["+", "+", "=", "="]
// Needed: ["+", "+", "=="]
Any help is much appreciated
What you need is to threat your symbols as regular strings. Iterate each symbol and search it in your source. If you find a range append it to a collection, get the substring at that location and search again your string from the index after the range upperBound. When you reach the end of the string searching fro that symbol remove the substrings from your source. Try something like this:
var source = "a + b + 3 == c"
let symbols = ["+", "-", "==", "!="]
var results: [Substring] = []
for symbol in symbols {
var startIndex = source.startIndex
var ranges: [Range<String.Index>] = []
while startIndex < source.endIndex,
let range = source[startIndex...].range(of: symbol) {
ranges.append(range)
results.append(source[range])
startIndex = source.index(after: range.upperBound)
}
for range in ranges.reversed() {
source.removeSubrange(range)
}
}
print(results)
this will print
["+", "+", "=="]
This problem is a parsing problem, which is to convert an input string into some kind of structured data. A nice way to do that is with parser combinators. Although the guts is complex, once set up, the example 'opStrings' function is simple to understand and it's easy to extend.
struct Parser<A> {
let run: (inout Substring) -> A?
static func always<A>(_ a: A) -> Parser<A> {
.init { _ in a }
}
static var never: Parser {
.init { _ in nil }
}
func map<B>(_ f: #escaping (A) -> B) -> Parser<B> {
Parser<B> { str -> B? in
self.run(&str).map(f)
}
}
func flatMap<B>(_ f: #escaping (A) -> Parser<B>) -> Parser<B> {
Parser<B> { str -> B? in
let original = str
let matchA = self.run(&str)
let parserB = matchA.map(f)
guard let matchB = parserB?.run(&str) else {
str = original
return nil
}
return matchB
}
}
func run(_ str: String) -> (match: A?, rest: Substring) {
var str = str[...]
let match = self.run(&str)
return (match, str)
}
}
func prefix(while p: #escaping (Character) -> Bool) -> Parser<Substring> {
Parser<Substring> { str in
let prefix = str.prefix(while: p)
str.removeFirst(prefix.count)
return prefix
}
}
func literalString(_ p: String) -> Parser<String?> {
Parser<String?> { str in
guard str.hasPrefix(p) else { return nil }
str.removeFirst(p.count)
return p
}
}
func literal(_ str: String) -> Parser<Void> {
literalString(str).map {
_ in ()
}
}
func oneOrMore<A>(_ predicate: #escaping (Character) -> Bool,
mapped f: #escaping (Substring) -> A) -> Parser<A> {
prefix(while: predicate)
.flatMap { $0.isEmpty ? .never : .always(f($0)) }
}
let oneOrMoreDecimals = oneOrMore((\.isNumber), mapped: Double.init)
.flatMap { d -> Parser<Double> in
guard let d = d else { return .never }
return .always(d)
}
func oneOf<A>(_ ps: [Parser<A>]) -> Parser<A> {
Parser<A> { str -> A? in
for p in ps {
if let match = p.run(&str) {
return match
}
}
return nil
}
}
let whitespace = oneOf([
literal(" "),
literal("\t"),
literal("\n")
])
let identifier = prefix(while: { ($0.isLetter && $0.isASCII) || $0.isNumber })
.flatMap { str -> Parser<String> in
guard let first = str.first else { return Parser.never }
return first.isNumber ? .never : .always(String(str))
}
let literalNumber = prefix(while: { $0.isNumber })
.flatMap { str -> Parser<Double> in
guard let value = Double(str) else { return Parser.never }
return .always(value)
}
func skip<T, U>(_ p: Parser<T>) -> Parser<U?> {
p.map { _ in nil }
}
struct Example {
enum Token {
case minus
case plus
case equal
case equalEqual
case notEqual
case identifier(String)
case number(Double)
}
enum Error: Swift.Error {
case unexpectedInput
}
let input: String
func run<T>(_ parsers: [Parser<T?>]) throws -> [T] {
var source = Substring(input)
var results: [T] = []
while let token = oneOf(parsers).run(&source) {
if let token = token {
results.append(token)
}
}
guard source.isEmpty else {
throw Error.unexpectedInput
}
return results
}
func tokenize() throws -> [Token] {
let tokenizers: [Parser<Token?>] = [
literal("-").map { .minus },
literal("+").map { .plus },
literal("==").map { .equalEqual },
literal("!=").map { .notEqual },
literal("=").map { .equal },
identifier.map { .identifier($0) },
oneOrMoreDecimals.map { .number($0) },
skip(whitespace)
]
return try run(tokenizers)
}
func opStrings() throws -> [String] {
try run([
literalString("-"),
literalString("+"),
literalString("=="),
literalString("!="),
literalString("="),
skip(identifier),
skip(oneOrMoreDecimals),
skip(whitespace),
] as [Parser<String?>])
}
}
do {
let example = Example(input: "a + b + 3 == c")
// let tokens = try example.tokenize()
print(try example.opStrings()) // ["+", "+", "=="]
}
catch {
print(error)
}

If Statement with Array Variables Out of Range Swift

I'm trying to search a user's contacts but one of my if statements is broken. It checks if the character at a certain index of the input matches the character of the same index of the givenName, familyName, and phoneNumber. My problem is that sometimes the number of characters entered is more than the number of characters in the givenName, familyName, or phoneNumber. This makes my if statement compare the character at a certain index of the input and the character at an index that doesn't exist. For instance, it compares charInput[2] to charGivenName[2] but charGivenName only has values at charGivenName[0] and charGivenName[1]. Since charGivenName[2] doesn't exist, my apps crashes.
Does anyone know how to fix this?
#IBAction func contactTextFieldChanged(_ sender: Any) {
if contactTextField.text != "" {
contactTableView.isHidden = false
let request = CNContactFetchRequest(keysToFetch: keys as! [CNKeyDescriptor])
do {
try contactStore.enumerateContacts(with: request){
(contact, stop) in
self.contacts.append(contact)
for phoneNumber in contact.phoneNumbers {
if phoneNumber.value.stringValue != ""{
let charGivenName = Array(contact.givenName)
let charFamilyName = Array(contact.familyName)
let charNumber = Array(phoneNumber.value.stringValue)
let charInput = Array(self.contactTextField.text!)
var matchBool = false
for inputCount in (0...charInput.count - 1) {
if charNumber[inputCount] == charInput[inputCount] || charGivenName[inputCount] == charInput[inputCount] || charFamilyName[inputCount] == charInput[inputCount]{
matchBool = true
} else {
matchBool = false
break
}
if matchBool == true {
print("\(contact.givenName) \(contact.familyName) \(phoneNumber.value.stringValue)")
}
}
}
}
}
} catch {
print("Error fetching contacts")
}
}
if contactTextField.text == ""{
contactTableView.isHidden = true
}
}

How to append custom object to an array in Swift?

How to append custom class object to an array in Swift?
Below is my code, but it shows error.
Error:
"Cannot assign value of '()' to type [PhotoVC]"
Code:
var photoVCs = [PhotoVC]()
for index in 0 ..< photos.count {
if let vc = getPhotoController(index) {
photoVCs = photoVCs.append(vc)
}
}
func getPhotoController(index: Int) -> PhotoVC? {
if index < 0 || index == NSNotFound {
return nil
}
else if index == photos.count {
return nil
}
let PhotoVCID = "PhotoVCID"
if let storyboard = storyboard,
pageVC = storyboard.instantiateViewControllerWithIdentifier(PhotoVCID) as? PhotoVC {
pageVC.photoName = photos[index]
pageVC.photoIndex = index
return pageVC
}
return nil
}
I should be able to do it, but what's the problem?
append does not return anything. Remove the assignment:
photoVCs = photoVCs.append(vc) // wrong
photoVCs.append(vc) // ok

Resources