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)
}
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
}
}
Spent all day on this one. I have an array of expenses that populates my tableview. Users can select a row from the tableview to edit the expense, which includes the name, the amount, the due date, etc.
I don't want users to be able to enter in a duplicate name. What I mean is, I want to check their input against the array to make sure the name doesn't already exist. This sounds simple to me, but for some reason I can't get it to work.
This is what I've tried:
CODE #1:
var duplicateCount = 0
for item in Expense.expensesArray.filter({ $0.ownerName == currentUserName }).filter({ $0.category == expense?.category }) {
if item.expenseName != expense?.expenseName {
print("no duplicates found in \(item.expenseName). moving on")
} else if item.expenseName == expense?.expenseName {
duplicateCount += 1
print("duplicate found in \(item.expenseName)")
}
}
CODE #2: (which is pretty similar to #1)
var duplicatesAmount: Int = 0
for expense in Expense.expensesArray.filter({ return $0.ownerName == currentUserName }).filter({ $0.category == expense?.category }) {
if expense.expenseName.lowercased() == expenseNameTextField.text?.lowercased() {
duplicatesAmount += 1
print("POTENTIAL DUPLICATE #\(duplicatesAmount): ",expense.expenseName)
}
}
// if there is more than one expense in the array with the same name, return true
if duplicatesAmount > 1 {
return true
} else {
return false
}
EDIT: The code runs whether there's a duplicate or not. It never sees the duplicate, even if I purposely put one in.
UPDATE: This worked.
if expense?.expenseName == expenseNameTextField.text {
updateExpenseInfo()
} else {
// iterate over the array and see if new name is found anywhere
let dupArray = Expense.expensesArray.filter({ $0.ownerName == currentUserName && $0.category == expense?.category && $0.expenseName == expenseNameTextField.text })
if dupArray.isEmpty {
updateExpenseInfo()
} else {
duplicateNameAlert()
}
}
You can do it like this:
struct Object {
let name: String
let amount: Int
init(name: String = "", amount: Int = 0) {
self.name = name
self.amount = amount
}
}
let a = Object(name: "Foo", amount: 10)
let b = Object(name: "Bar", amount: 20)
let array = [a, b]
func contains(value: String) -> Bool {
return array.filter({ $0.name == value }).isEmpty
}
print(contains(value: "Foo")) // true
print(contains(value: "Test")) // false
So you basically just add the contains function and return array.filter({ $0.name == value }).isEmpty. If it returns true then save otherwise donĀ“t save. The value you pass it the entered value.
I am currently using 2 arrays:
let letters:[Character] =
[" ","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","!","#","#","$","%","^","&","*","(",")","1","2","3","4","5","6","7","8","9","0","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","E","S","T","U","V","W","X","Y","Z"]
let cypher:[Character] = ["o","p","q","r","a","b","c","d","e","f","g","h","i","u","v","w","x","y","z","j","k","l","m","n","s","t","$","#","#","!","&","%","^","*","("," ",")","5","7","2","9","8","0","1","3","2","4","Q","W","E","R","T","Y","U","I","O","P","A","S","D","F","G","H","J","K","L","Z","X","C","V","B","N","M"]
Both 73 characters.
I am using this line of code to encode the inserted text:
var encode:[Character:Character] = [:]
for (index, letter) in letters.enumerated() { encode[letter] = cypher[index] }
let encodeStep1 = String(insertedText.characters.map({ encode[$0] ?? $0 }))
randomNumber = Int(arc4random_uniform(9) + 1)
var encodeStep2 = cypher.rotate(shift: randomNumber)
for (index, letter) in letters.enumerated() { encode[letter] = encodeStep2[index] }
let final = String(encodeStep1.characters.map({ encode[$0] ?? $0 }))
Decode:
var decode:[Character:Character] = [:]
let step1 = cypher.rotate(shift: (randomNumber))
for (index, letter) in step1.enumerated() { decode[letter] = letters[index] }
let step1Decoded = String(insertedEncryptedText.characters.map({ decode[$0] ?? $0 }))
for (index, letter) in cypher.enumerated() { decode[letter] = letters[index] }
let step2Decoded = String(step1Decoded.characters.map({ decode[$0] ?? $0 }))
Rotate function:
extension Array {
func rotate(shift:Int) -> Array {
var array = Array()
if (self.count > 0) {
array = self
if (shift > 0) {
for i in 1...shift {
array.append(array.remove(at: 0))
}
}
else if (shift < 0) {
for i in 1...abs(shift) {
array.insert(array.remove(at: array.count-1),at:0)
}
}
}
return array
}
}
For some very odd reason, number "3" is often shown as a number "9" when decoded. From what I have seen, the problem occurs at step2Decoded. I just can not figure out what I am doing wrong. This is however a part of the code, if I need to post more I can do that.
It's because you have a typo. Your cypher array has two "2"s in it, one at cypher[39] and another 6 indices further at cypher[45]. When you're decoding in the final step you're expecting decoded["2"] to map to the value "3" in the letters array, which it does, but it's getting overwritten when it hits the "2" again setting it to the value 6 indices further in the letters array which has a value of "9".
I assume you want to change that second "2" value in the letters array to "6" instead (since I noticed there was no "6" in there). That would solve your problem.