how do i make sure users can't enter in duplicates? - arrays

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.

Related

How do I concisely update a highly nested object model using swift?

I am writing a function which will dig into the deepest level of a object hierarchy and update a particular value. For reference, this is how messy a regular iteration turned out to be:
func selectedStateUpdated(data: MyObject, tabId: String, rowId: String){
for currIndex in 0..<self.model.data[0].data[0].filters.count{
if(self.model.data[0].data[0].filters[currIndex].headerName == data.headerName){
for tabIdx in 0..<model.data[0].data[0].filters[currIndex].data.count {
if(self.model.data[0].data[0].filters[currIndex].data[tabIdx].id == tabId){
for rowItemIdx in 0..<self.model.data[0].data[0].filters[currIndex].data[tabIdx].data.count {
if(self.model.data[0].data[0].filters[currIndex].data[tabIdx].data[rowItemIdx].id == rowId){
self.model.data[0].data[0].filters[currIndex].data[tabIdx].data[rowItemIdx].isSelected.toggle()
}
}
}
}
}
}
}
What is the best way to dig this deep into my data model and update a particular attribute? I am trying to update the model object which resides in the class of this function as a global object. Given that iterable objects in my object model are immutable in for-loops, is there a better way of approaching this while making sure I iterated into the proper
At its core, this is a data structure problem. You're doing a linear search through all your data to find something with a specific headerName, tabId, rowId. It's complicated, messy and slow (O(headerCount * tabCount * rowCount)!)
If dictionaries were used, this could just be:
self.model.data[0].data[0]
.filters[headerName]
.data[tabId]
.data[rowId]
.isSelected
.toggle()
If the arrays must stay, then you can clean this up a lot by extracting common parts to named local variables. Inverting the if statements (in this case, into guard statements`) also saves a ton on nesting:
func selectedStateUpdated(headerName: String, tabId: String, rowId: String){
let data = self.model.data[0].data[0] // TODO: name me better
for currIndex in 0..<data.filters.count {
let filter = data.filters[currIndex]
guard filter.headerName == headerName else { return }
for tabIdx in 0..<filter.data.count {
let tab = filter.data[tabIdx]
guard tab.id == tabId else { continue }
for rowItemIdx in 0..< tab.data.count {
let rowItem = tab.data[rowItemIdx]
guard rowItem.id == rowId else { continue }
self.model.data[0].data[0]
.filters[currIndex]
.data[tabIdx]
.data[rowItemIdx]
.isSelected
.toggle()
}
}
}
}
It could be further improved by using zip to get indices and items in one shot:
func selectedStateUpdated(headerName: String, tabId: String, rowId: String){
let data = self.model.data[0].data[0] // TODO: name me better
for (currIndex, filter) in zip(data.filters.indices, data.filters) {
guard filter.headerName == headerName else { return }
for (tabIdx, tab) in zip(filter.data.indices, filter.data) {
guard tab.id == tabId else { continue }
for (rowItemIdx, rowItem) in zip(tab.data.indices, tab.data) {
guard rowItem.id == rowId else { continue }
self.model.data[0].data[0]
.filters[currIndex]
.data[tabIdx]
.data[rowItemIdx]
.isSelected
.toggle()
}
}
}
}
Basically what you are doing when you say ".id == tabId" is a filter function. This code was quite messy. But i think i managed to figure it out:
func selectedStateUpdated(data: [MyObject], tabId: String, rowId: String) {
let filteredResult = self.model.data[0].data[0].filters
.filter({$0.headerName == data.headerName &&
$0.data.filter({$0.id == tabId}) &&
$0.data.filter({$0.id == tabId &&
$0.data.filter({$0.id == rowId})
})
})
if filteredResult.count > 0 {
filteredResult.first!.isSelected.toggle()
}
}

Is There a Better Way To Filter An Array With Multiple Conditions

I am using a dropdown menu help filter an array and then place the data from the array back into a collectionView but, when I attempt to attempt to run the function that should sort and reload the collectionView, the screen goes blank.
I've tried to make set a baseline and then filter the array.
extension RestaurantDetails: menuViewDelegate {
func didChangeMealSize(mealSizeText: String) {
self.mealSize = mealSizeText
print("Usage", self.mealSize)
}
func didChangeDrink(drinkName: String) {
self.drink = drinkName
print("drink", self.drink)
}
func didChangeProductType(productType: String) {
self.productFilter = productType
print("Filter", self.productFilter)
}
func didchangeSwitch(FriesSwitch: UISwitch) {
if FriesSwitch.isOn == true {
self.FriesOnly = true
print("FRIES Only")
} else {
self.FriesOnly = false
print("Drink & FRIES")
}
func didendSelection(button: UIButton) {
print("ran Here")
if (SegmentType == "All" && FriesSwitch == false && mealSize == RestaurantDetails.allProductIdentifier && productFilter == RestaurantDetails.allProductIdentifier && price == 0.0) {
filteredProducts = products
} else {
filteredProducts = products.filter { ($0.usage?.lowercased() ?? "") == bestFor}
}
productCollection.reloadData()
}
I want to filter by selected: Meal Size, Drink, and With Or Without Fries. When didendSelection is run, All the properties of the selected variables print but, the collectionView comes back empty.

Swift 2 String Array remove Spesific Index Error

I have String Array;
var students = [String]()
and my students array output :
students[0]:102----3----
students[1]:102-2018.07.24-4--6---
students[2]:103--5--4--
students[3]:34-2018.07.24---3-4--6--
students[4]:34--6---5----4--
I want to remove students[0] so students[1] same id with students[0] and students[0] dont have date.
I want to remove students[4] so students[3] same id with students[4] and students[4] dont have date.
In Swift 2 how can i do it ?
Must be output
students[0]:102-2018.07.24-4--6---
students[1]:103--5--4--
students[2]:34-2018.07.24---3-4--6--
My test codes;
var count = 0
for mystudents in self.students {
let explode1 = "\(mystudents)".componentsSeparatedByString("-")
let explode2 = "\(mystudents)".componentsSeparatedByString("-")
if (explode1[0] == explode2[0]) { // HERE if equal same ids
if (explode1[1] == "" || explode2[1] == ""]){
self.students.removeAtIndex(count++) // HERE gives fatal error: Index out of range
}
}
}
And
i didnt fix tihs about 5 days , who will fix this i will give 500 reps after 2 days with bounty
I am suggesting the below solution on the understanding that you want to remove an object that has only id and it matches with other object that has same id and date. If my understanding is wrong, please let me know,
let students = ["102----3----",
"103--5--4--",
"102-2018.07.24-4--6---",
"34-2018.07.24---3-4--6--",
"34--6---5----4--"]
let filtered = students.filter { (student) -> Bool in
let id = student.characters.split{$0 == "-"}.map({ String($0) })
let exists = students.filter({ (other) -> Bool in
if student == other {
return false
}
let otherId = other.characters.split{$0 == "-"}.map({String($0) }).first!
return id.first! == otherId
}).first
if exists != nil, id.count > 1 {
return id[1].characters.filter({ $0 == "."}).count == 2
}
return true
}
print(filtered)
Output:
["103--5--4--", "102-2018.07.24-4--6---", "34-2018.07.24---3-4--6--"]

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 break and return in a loop

I try to access a value in an array of dictionary. The dataArray looks like this:
struct Person {
let name: String
let lastName: String
let age: Int
}
let person1: Person = Person(name: "Steven", lastName: "miller", age: 23)
let person2: Person = Person(name: "jana", lastName: "drexler", age: 31)
let person3: Person = Person(name: "hanna", lastName: "montana", age: 56)
var dataArray = [Person]()
dataArray.append(person1)
dataArray.append(person2)
dataArray.append(person3)
Now I want to access the age of jana. If I´m doing this:
func getAge() ->Int {
var age: Int = 0
for items in dataArray {
while items.name == "jana" {
age = items.age
return age
break // This break will never be executed because of return.
}
break // setting the break here, the loop will break after first round
}
return age
}
the loop will stop after the first round. (it works only for steven, because he is in the first round of the lopp.) The array is quite long, so i need to stop the loop after the first match. Setting break after return, it will not be executed because of return. Setting return after break, it´s the same. Any suggestions?
For vadian:
class AGE {
func getAge() -> Int? {
dataArray.append(person1)
dataArray.append(person2)
dataArray.append(person3)
// Cannot call value of non-function type `Person`
return dataArray.first(where: { $0.name == "john" }?.age
}
}
How about this? No need for the inner loop (which was never looping anyway).
func getAge() ->Int {
for item in dataArray {
if item.firstName == "jana" {
return item.age
}
}
return 0
}
As a side note, a dictionary might be a better way to store the data to make lookups more efficient.
There is no need for the while loop or the break statements.
If you want to iterate the loop looking for the age of a specific person, all you need is:
func getAge() -> Int? {
for items in dataArray {
if items.name == "jana" {
return items.age
}
}
return nil
}
Note the updated return value to be an optional. A nil return value means the person wasn't found. Don't use 0 as a magic value.
It's also bad to hardcode the name. Pass it as a parameter. And use better variable names:
func getAge(of name: String) -> Int? {
for person in dataArray {
if person.name == name {
return person.age
}
}
return nil
}
if let age = getAge(of: "jana") {
print("age is \(age)")
} else {
print("Name not found")
}

Resources