Array search function not working in Swift - swift

I confess feeling little stupid, but after hours of trying I have to ask:
class AGE {
static func getAge() -> Int {
var age: Int
for items in dataArray {
if items.name == "Steven" {
age = items.age
}
else {
age = 0
}
}
// variable "age" used before being initialized - WHY?
return age
}
}
I even tried to set var age: Int = 0 at the beginning, but then the function return 0. I hope someone could forgive me this basic question at this point. Any help appreciated.
To make it more clear how dataArray looks like:
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)
Update
Trying to assemble the essence of all answers, the solution has to look like this:
class AGE {
static func getAge() ->Int {
var age: Int = 0
for items in dataArray {
while items.firstName == "Steven" {
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
}
}
This code works, (with the second break) but only for the first loop. The question remaining is, how to set up return and break after the loop hits its target. Either return or break will prevent the other step.

The possible program flow paths leading to the initialization of age before its return are not exhaustive. In case dataArray is empty, the body of the for ... in loop will never be entered, and age will never initialized. The simplest approach to resolve this issue in your particular example would be simply initialize age to 0 upon it's declaration.
Regarding the "unexpected" 0 return even in case dataArray contains an element with a name property valued "Steven": for each pass in the for ... in loop that does not contain an element with a name property valued "Steven", you will reset the value of age to 0. This means that only if the "Steven" valued element is the last of the dataArray will the non-0 age value persist. I don't really see any reason to ever set age to 0 again beyond initialization, so you could resolve this by e.g. performing an early exit (break) from the for loop in case of a match. Alternatively, depending on what you want to achieve, simply return items.last_updated from the method upon first match (just note that early return statements could affect performance).

I'm not Swift developer but I doubt these logics are correct. Assuming you initialise age to 0 as suggested in other answers, after you find the element that matches "Steven" into the array, you should exit. Otherwise the next iteration could set age to zero again.
Remember, by default for statement iterates through all elements. If you want to iterate till a condition is met, then a while may be more suitable.
I've read a tutorial and tested this code on line using swift sand box. Sharing my code on this link.
class AGE {
static func getAge() -> Int {
var age = 0
var found = false
var current = 0
var allElementsChecked = (dataArray.count == 0) //is false unless array is empty
while !found && !allElementsChecked {
found = (dataArray[current].name == "Steven")
allElementsChecked = (current == dataArray.count-1)
current += 1
}
if found
{
age = dataArray[current-1].age //minus 1 to undo last increment within loop
}
return age
}
}
When comes out from the loop that may be because either "Steven" was found or all elements were checked and not found.

As mentioned in the other answers the issue is that the variable will never be set if the array is empty.
Apart from that your code is inefficient and returns only the expected result if Steven is the last item in the array. Otherwise the variable age gets overwritten with 0.
Nevertheless in Swift there are more convenient methods to filter an item in an array than a repeat loop, for example first(where:
class AGE {
static func getAge() -> Int {
return dataArray.first(where: { $0.name == "Steven" })?.age ?? 0
}
}
or it might be preferable to return nil if Steven is not in the array
class AGE {
static func getAge() -> Int? {
return dataArray.first(where: { $0.Name == "Steven" })?.age
}
}
PS: last_updated in your code is not related to the struct Person at all.

You need to initilize it in the beginning:
var age: Int = 0
If for some reason your for-loop don´t execute then you can´t set your age variable when you only have declared it as var age: Int and not initilized it.
And regarding:
I even tried to set var age: Int = 0 at the beginning, but then the
function return 0
That means that you had intilizied your age variable to 0 and the for-loop where never executed and the return will be 0.

I tested Vadian's answer and it seems to be the correct one as per your requirements.
Nonetheless, here are other suggestions:
First, create another instance of your array based on your Person Struct:
var filteredPerson = [Person]()
If you want to search your dataArray for the age based on the first name:
func getAge(of name: String){
filteredPerson = dataArray.filter { $0.name == name }
print (filteredPerson[0].age) }
To get the age value of "Steven":
getAge(of: "Steven")
You'll get the Result: 23.
Note that you can also just get the Int value via return:
func getAge(of name: String) -> Int {
filteredPerson = dataArray.filter { $0.name == name }
return filteredPerson[0].age }
On the other hand, if you have lots of Persons in your list with the same name, and you want to get the age value of a specific Person, just add the lastName parameter:
func getAge(name: String, lastName: String){
filteredPerson = dataArray.filter { $0.firstName == name && $0.lastName == lastName }
print (filteredPerson[0].age) }
And then get the age value via calling:
getAge(name: "Steven", lastName: "Miller")
Hope this helped someone in some way :)

Related

How can I compare two different types of data (String and Int) using readLine() on Swift?

everyone! I'm a new member of Stack Overflow, just like I'm beginner on swift programming. I'm making this post to find out a solution for the following case:
I'm creating on Swift an app using the Command Line Tool for inputing data. The app basically works as an authenticator. As an example, if someone types USA for the country name and the age is 17, so the program will return a message like "You can not apply to this position". Otherwise, if the country name is USA and the age is equal or higher than 18, so the message returns is "You can forward to the next step". I've tried many times to set this conditions, but it's not working. I'm already knows that the function readLine() is an Optional String, but how can I make my program work correctly? It follows my code above to you understanding my thoughts.
I really appreciate any help. Again, I'm beginner and I'm already studying Swift languages, but I'm seeking some solution that handles Integers and Strings and comparing both data types. Thank you very much!
My code is:
import Foundation
print("Enter your country: ")
var country = readLine()
print("Enter your age: ")
var age = readLine()
if var country, var age = readLine(){
if country == "USA" && age < "18" {
print("You're not allowed to apply to this position.")
} else {
print("You can forward to the next step.")
}
}
PS: As you see, I'm using wrongly the variable age as an String, but I want to convert it to an Int type and then, check if the country name is the same than the value I assigned to or the age is equal or higher than 18. But not found a solution so far.
I'm trying to find a solution that compares two different types on Swift, using Command Line Tool and the readLine() function to check if a condition is true or not. If it's true, an output message will show that the user can proceed to the next step, otherwise he will not be permitted to follow. I'm keeping for an explanation on internet since few days, but not found anything that might help me. I hope to get some help using the Stack Overflow forum to some useful answer.
First one, readline() means you read the current to the end of current line . As your code when you check condition you call readLine() again. The wrong part is here.
I recommend you to read first then do all your logic. You just need to read only one time at first
print("Enter your country: ")
var country = readLine()
print("Enter your age: ")
var ageString = readLine()
Next, check if it is nil or not ( because option value which is value can be nil)
if country == nil || ageString == nil {
print("Error because one of them is nil")
fatalError()
}
Then check if can convert to Int or not. Reach here you sure that the ageString is not nil because you have checked above. So you just simply convert
guard let ageString = ageString else {
print("Error age nil")
fatalError()
}
guard let age = Int(ageString) else {
print("Error age not a number")
fatalError()
}
Then after all, you just check your condition
Full code will be like this
print("Enter your country: ")
var country = readLine()
print("Enter your age: ")
var ageString = readLine()
// check nil first if nil return or do something first and not get to the rest
if country == nil || ageString == nil {
print("Error because one of them is nil")
fatalError()
}
guard let ageString = ageString else {
print("Error age nil")
fatalError()
}
guard let age = Int(ageString) else {
print("Error age not a number")
fatalError()
}
if country == "USA" && age < 18 {
print("You're not allowed to apply to this position.")
} else {
print("You can forward to the next step.")
}
Other methods is use if let to achieve so no force unwrap
print("Enter your country: ")
var country = readLine()
print("Enter your age: ")
var ageString = readLine()
// check nil first if nil return or do something first and not get to the rest
if country == nil || ageString == nil {
print("Error because one of them is nil")
fatalError()
}
if let ageString = ageString {
if let age = Int(ageString) {
if country == "USA" && age < 18 {
print("You're not allowed to apply to this position.")
} else {
print("You can forward to the next step.")
}
} else {
print("Error age not a number")
fatalError()
}
}
SOLUTION SOLVED!
Hey, guys, first of all I want to thank you all for your helpful answers, which helped me a lot. I've got finally a solution, and am going to share it with you.
What did I done? I just created two variables, one String and another Integer. Then, using the if var to force unwrapping of the Int variable, I've made an if statement to check if the both conditions are true (in the case, if the person is from USA and has an age equals or higher than 18). Now, the program is running on the same way I just wanted. If you are from USA but has no 18 years, output returns a message that you can not apply. Otherwise, you're able to forward.
I'll let the code above. If you want to make some comments or any suggestions, it'll be welcome.
Again, thank you very much for all your answers.
var countryCheck = "USA"
var ageCheck: Int = 18
print("Enter your country: ")
var country = readLine()
print("Enter your age: ")
var age = readLine()
if var countryCheck = country, var ageCheck = Int(age!) {
if countryCheck == "USA" && ageCheck >= 18 {
print("You can apply.")
} else {
print("You can not apply to this position.")
}
}
I hope this help you :)
import Foundation
print("Enter your country: ")
if let country = readLine() {
if let num = Int(country) {
print(num)
}
}
let country = readLine()
let age = readLine()
if let USA = country,
let num1 = Int(USA),
let num2 = Int(USA) {
print("The age of \(num1) and \(num2) is \(num1 + num2)")
}

first or filter function of swift array doesnt give the right reference object

I have defined a struct in my viewcontroller before my viewdidload
struct CustomFilterButton {
var Id : Int = 0;
var Name : String = "";
var selected : Bool = false;
}
then I create reference for it in global
var customButtons = [CustomFilterButton]();
then in my viewdidload I appended some customFilterButton objects in customButtons array
customButtons.append(CustomFilterButton.init(Id: 1, Name: "A", selected: false))
customButtons.append(CustomFilterButton.init(Id: 2, Name: "B", selected: false))
customButtons.append(CustomFilterButton.init(Id: 3, Name: "C", selected: true))
customButtons.append(CustomFilterButton.init(Id: 4, Name: "D", selected: false))
in viewdidload or in any other function when I try to get an object in array using first or filter and change it, but it doesnt work.
print(customButtons[0].selected);
print("--")
var bt = customButtons.first{
$0.Id == 1
}
bt?.selected = true;
print(bt?.selected);
print(customButtons[0].selected);
here is the result
false
--
Optional(true)
false
the same applies to filter also!
Am I missing something or am I doing something wrong?
Note: I need to get the object that first or filter found and change it, not the hard copy of it
When you deal with a Struct, you have to understand that it is a value type.
So, that means that everytime you pass a value around, it's a COPY of the value and not the reference
And when you do this:
var bt = customButtons.first{
$0.Id == 1
}
You're asking the compiler to retrieve a copy of the CustomFilterButton whose Id is 1 and assign the copy to your bt variable.
To tackle this, you can access the element by its index and directly modify its value without passing it around (assigning to a new variable)
// Get the index of the element you're trying to modify
if let indexYouWantToModify = customButtons.firstIndex(where: {$0.Id == 1}){
// Modify it directly through its index
customButtons[indexYouWantToModify].selected = true
}
Oh, and though changing your Struct to a Class works for you, I think it's unnecessary just for this little use case. Structs and Classes hold their own benefits and trade-offs. I'm not sure what you're planning to do over this CustomFilterButton over the long run so I suggest you to read this article and decide for yourselves!
var bt = customButtons.first{
$0.Id == 1
}
At this moment bt has no any relation to your customButtons[0], its value was copied.
Access to item by index
print(customButtons[0].selected);
print("--")
var offset = 0
var bt = customButtons.enumerated().first{
if $0.element.Id == 1 {
offset = $0.offset
return true
}
return false
}
customButtons[offset].selected = true;
print(customButtons[0].selected);
You should use class instead of struct.

how to read data into object array in Swift 4

class Employee{
var id:Int
var name:String
var salary:Int
init(){
self.id=0
self.name=""
self.salary=0
}
func getInfo(){
self.name=readLine()!
self.id=Int(readLine()!)!
self.salary=Int(readLine()!)!
}
}
var count=0
var flag="y"
var empData:[Employee]=[]
repeat{
count+=1
empData[count]=Employee()
empData[count].getInfo()
flag=readLine()!
}while(flag=="y") `
I have a class Employee with properties id , nam and salary. The function getInfo() is used to get information from user. I want to read data until the flag!="y" . I am getting index out of range error.
What is the right way of inputting data? Can we index the objects ?
You need to append to your array to make it increase in size. Replace
empData[count]=Employee()
with
empData.append(Employee())
to avoid index out of range error
Update
To make your code a little less horrible I would do
repeat {
var employee = Employee()
employee.getInfo()
empData.append(employee)
flag=readLine()!
}while( flag == "y" )
The subscript operator cannot be used to add elements to an array index which doesn't exist yet. You either need to initialize the array with an element count if you know at the time of initialization how many elements your array will have or use the append operator to add new elements to the array after the last index.
You don't even need the count variable, as you can simply access empData.last safely after calling append and adding a new Employee to the Array.
var flag="y"
var empData:[Employee]=[]
repeat {
empData.append(Employee())
empData.last!.getInfo()
flag=readLine()!
} while(flag=="y")
I would advise you to seriously reconsider your implementation as it is really unsafe at the moment. You are not validating user input in any way, hence your getInfo function can easily cause runtime errors if the user input is not in the expected form. Moreover, creating an empty initializer for Employee doesn't make sense, you could simply create a failable initializer, where you read the input and if the input is not of the correct form, make the initializer return nil.
class Employee{
let id:Int
let name:String
let salary:Int
init?(){
guard let name = readLine() else { return nil }
self.name = name
guard let idString = readLine(), let id = Int(idString) else { return nil }
self.id = id
guard let salaryString = readLine(), let salary = Int(salaryString) else { return nil}
self.salary = salary
}
}
var flag="y"
var empData:[Employee]=[]
repeat {
if let employee = Employee() {
empData.append(employee)
} else {
// Display error message to the user
}
flag=readLine() ?? ""
} while(flag=="y")

Trouble using UILocalizedIndexedCollation to filter a table view with the inputed user's name.

I'm new to this forum! I am having a little bit of trouble sorting my Person's array with the use of UILocalizedIndexedCollation.
Code
I created the following class:
#objc class Person : NSObject {
#objc var firstName: String!
#objc var lastName: String!
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}
With the help of this class, I created a bunch of instances to be displayed and sorted alphabetically on the table view by their last names.
For me to do so, I created the following function:
func partitionObjects(array:[AnyObject], collationStringSelector:Selector) -> ([AnyObject], [String]) {
var unsortedSections = [[AnyObject]]()
//1. Create a array to hold the data for each section
for _ in self.sectionTitles {
unsortedSections.append([]) //appending an empty array
}
//2. Put each objects into a section
for item in array {
let index:Int = self.section(for: item, collationStringSelector:collationStringSelector)
unsortedSections[index].append(item)
}
//3. sorting the array of each sections
var sectionTitles = [String]()
var sections = [AnyObject]()
sectionTitles.append("Frequently Requested")
for index in 0 ..< unsortedSections.count { if unsortedSections[index].count > 0 {
sectionTitles.append(self.sectionTitles[index])
sections.append(self.sortedArray(from: unsortedSections[index], collationStringSelector: collationStringSelector) as AnyObject)
}
}
return (sections, sectionTitles)
}
With this function, I am able to assign my array of Person and array of titles to the returning values of the function:
var sectionTitles = [Person]()
var personsWithSections = [[Person]]()
var contacts = [Person]()
#objc func setUpCollation(){
let (arrayPersons, arrayTitles) = collation.partitionObjects(array: self.persons, collationStringSelector: #selector(getter: Person.lastName))
self.personsWithSections = arrayPersons as! [[Person]]
self.sectionTitles = arrayTitles
}
With this, I am able able to set up my whole table view and it works great. I get every person sorted up by their last name to their corresponding alphabetical section.
The Issue, Specifically
The problem is that if the user has only inputed their first name and not their last name, that Person object gets appended to the last section of the array, where objects with last names that start with numbers or special characters go.
How can I append these object's with only first names to the corresponding alphabetical
section if the user doesn't input a last name? The same goes with
numbers.
As you can see, my collationsStringSelector takes Person.lastName, I was thinking maybe I could create a computed variable that would return the right person's property under some conditions? I am not sure how I would do so. It would be nice to have some guidance or help!
ps: NEW to the forum! if I'm doing something wrong let me know guys! thanks

how can I return a result from a function into a variable?

I have a variable :
var name = [Nick,John,Peter]
and a function:
func name(){
var personname = []
.......
return personname
}
the result of the function are the names of the people Nick,John,Peter. How can I return the result into the variable.I mean I do not want to write the names manually I want to populate them from the function, because the function display all of the names. I am beginner and I will be glad if you show me the right syntaxis.
var name = [????] = [personname()] or how????
You should use -> to return something from a function. Here is an example:
static let people = ["Nick", "John", "Peter"]
func getPerson(index: Int) -> String
{
return self.people[index] //index is number of a person, 0 will be the first one, 1 is second, etc.
}
func showTeam()
{
// some code
print("This is a first boy: \(self.getPerson[0])")
// some code
}