Extract value from struct that meet condition - swift

I have a struct for different animals, and values for these animals. Im adding animals to it.
struct Animal {
var type: String
var weight: String
var cost: String
}
var animals = [Animal]()
func addAnimal(type: String, weight: String, cost: String){
animals.append(Animal(type: type, weight: weight, cost: cost))
}
addAnimal("monkey", "80", "300")
addAnimal("zebra", "200", "500")
addAnimal("monkey", "50", "250")
I want to say, if type == "monkey" then return all weights for monkeys. In this example I would want the code to return values "80" and "50".
I'm new to coding so any advice on this would be helpful. Thank you

You can combine filter and map to accomplish what you want as follow:
let monkeysWeights = animals.filter{$0.type == "monkey"}.map{$0.weight}
println(monkeysWeights) // ["80", "50"]

Related

Swift iOS -How come I can loop through an array of class objects and make property changes but not structs [duplicate]

This question already has answers here:
Is Swift Pass By Value or Pass By Reference
(10 answers)
Swift can change struct declared with let if using an index but not if using a loop
(3 answers)
Closed 3 years ago.
If I loop through an array of Class objects I can make changes to a property on it
class Country {
var name: String?
var region: String?
init(name: String?, region: String?) {
self.name = name
self.region = region
}
}
let canada = Country(name: "Canada", region: "North America")
let mexico = Country(name: "Mexico", region: "North Ameria")
let france = Country(name: "France", region: "Europe")
let korea = Country(name: "Korea", region: "Asia")
var countryArr = [canada, mexico, france, korea]
// this works fine
let transformed = countryArr.map { $0.name = "Random" }
But if I try this with Struct objects I get
Cannot assign to property: '$0' is immutable
struct Country {
var name: String?
var region: String?
}
var canada = Country(name: "Canada", region: "North America")
var mexico = Country(name: "Mexico", region: "North Ameria")
var france = Country(name: "France", region: "Europe")
var korea = Country(name: "Korea", region: "Asia")
var countryArr = [canada, mexico, france, korea]
// this gets an error
let transformed = countryArr.map { $0.name = "Random" }
The issue is caused by the fact that structs are value types, so mutating any properties of the struct mutates the struct instance itself as well and the closure input arguments in map are immutable. So when you try to mutate a property $0 in the closure of map, you are trying to mutate $0 itself in case map is called on a collection of value types.
On the other hand, classes are reference types, so mutating a property of a class instance doesn't mutate the instance itself.
A solution for your problem is to create a mutable copy of the struct instance in the map, mutate its name property and return that. There are two solutions, if you have a small number of properties on your type, calling its memberwise initialiser is easier, but if you have a lot of properties and only want to mutate a few, copying the struct and then modifying the necessary properties is the better choice.
let transformed = countryArr.map { Country(name: "Random", region: $0.region) }
let transformed2 = countryArr.map { country->Country in
var copy = country
copy.name = "Random"
return copy
}

How can I properly display a much readable output using sorting?

The output of my code is all good, it is already sorted, but the problem is that, it contains some garbage value that I do not need, I will provide the example output of it.
Here is my code:
struct Student {
var id: Int = 0;
var name: String = String();
var course: String = String();
var GPA: Float = 0.0;
}
let student = [
Student(id: 201520032, name: "Ton Agnis", course: "BSITWMA", GPA: 3.69),
Student(id: 201620122, name: "Juan Cruz", course: "BSCSSE", GPA: 2.23),
Student(id: 201723214, name: "Pedro Sy", course: "BSITAGD", GPA: 2.87),
Student(id: 201418492, name: "Phot xPro", course: "BSCPE", GPA: 3.99)
]
func stud(get studs:[Student]){
print("Student No.\t\tID\t\tName\t\t\tCourse\t\tGPA")
for i in 0...studs.count - 1{
print("Student \(i+1) \t \(student[i].id)\t\(student[i].name)\t\t\(student[i].course)\t\t\(student[i].GPA)")
}
}
let x = student.sorted{ $0.GPA < $1.GPA }
stud(get: student)
print(x)
Here is the Output of the Given Code
As you can see the output displays some values that is not needed.
What I want to be displayed is a better readable sorted of values given.
Thank You!
If you make your custom classes conform to the CustomStringConvertible protocol (add a single computed variable, description, of type String) then when you print one of those objects it displays nicely formatted.
You could use the formatting of your print statement with tabs as the starting point.
The function stud is already printing your student array in a formatted way.
Remove print(x) at the end of the code you posted to get a clean output.
Edit:
Also if I understand correctly your needs, you want to print the sorted list of students by GPA. (x in your code)
You can do it by passing x to the stud function and by fixing the stud function to use the function parameter instead of student var.
struct Student {
var id: Int = 0;
var name: String = String();
var course: String = String();
var GPA: Float = 0.0;
}
let student = [
Student(id: 201520032, name: "Ton Agnis", course: "BSITWMA", GPA: 3.69),
Student(id: 201620122, name: "Juan Cruz", course: "BSCSSE", GPA: 2.23),
Student(id: 201723214, name: "Pedro Sy", course: "BSITAGD", GPA: 2.87),
Student(id: 201418492, name: "Phot xPro", course: "BSCPE", GPA: 3.99)
]
func stud(get studs:[Student]){
print("Student No.\t\tID\t\tName\t\t\tCourse\t\tGPA")
for i in 0..<studs.count {
print("Student \(i+1) \t \(studs[i].id)\t\(studs[i].name)\t\t\(studs[i].course)\t\t\(studs[i].GPA)")
}
}
let x = student.sorted{ $0.GPA < $1.GPA }
stud(get: x)

Delete array of structs from an array of structs Swift

I have an array of structs that looks like this:
struct minStruct {
var namn:String?
var imag:UIImage?
var rea:String?
var comp:String?
}
var structArr = [minStruct]()
If I would like to remove a specific struct from that array, I could simply do this:
var oneStruct = minStruct(namn: "Name", imag: UIImage(named: "Image"), rea: "Something", comp: "Something")
if structArr.filter({$0.namn == oneStruct!.namn}).count > 0 {
structArr = structArr.filter({$0.namn != oneStruct!.namn})
}
However, what I would like to do is to remove an array of structs from structArr. Something like this:
structArr = [minStruct(namn: "Name", imag: UIImage(named: "Image"), rea: "Something", comp: "Something"), minStruct(namn: "secondName", imag: UIImage(named: "secondImage"), rea: "Something2", comp: "Something2"), minStruct(namn: "thirdName", imag: UIImage(named: "thirdImage"), rea: "Something3", comp: "Something3")]
var arrToDelete = [minStruct(namn: "Name", imag: UIImage(named: "Image"), rea: "Something", comp: "Something"), minStruct(namn: "secondName", imag: UIImage(named: "secondImage"), rea: "Something2", comp: "Something2")]
So what I want to do is to delete all the items that are inside of arrToDelete from arrStruct. In this example, arrToDelete contains two of the three structs that structArr contains. I want to delete these two structs and keep the one struct that arrToDelete did not contain. I hope that I was clear enough!
Hashable
So we have struct. First of all let's make it Hashable
struct Element: Hashable {
var name: String?
var image: UIImage?
var rea: String?
var comp: String?
var hashValue: Int { return name?.hashValue ?? image?.hashValue ?? rea.hashValue ?? comp.hashValue ?? 0 }
}
func ==(left:Element, right:Element) -> Bool {
return left.name == right.name && left.image == right.image && left.rea == right.rea && left.comp == right.comp
}
Data
Next we have these 2 arrays
let elms : [Element] = [
Element(name:"a", image:nil, rea:nil, comp:nil),
Element(name:"b", image:nil, rea:nil, comp:nil),
Element(name:"c", image:nil, rea:nil, comp:nil)
]
let shouldBeRemoved: [Element] = [
Element(name:"b", image:nil, rea:nil, comp:nil),
Element(name:"c", image:nil, rea:nil, comp:nil)
]
Solution #1
If you DO NOT care about the original sorting you can use
let filtered = Array(Set(elms).subtract(shouldBeRemoved))
Solution #2
If you DO care about the original sorting
let shouldBeRemovedSet = Set(shouldBeRemoved)
let filtered = elms.filter { !shouldBeRemovedSet.contains($0) }
Why didn't I just write this?
let filtered = elms.filter { !shouldBeRemoved.contains($0) }
The line above is a correct solution. However invoking contains on
Array is generally slower (usually n/2 checks need to be performed)
than invoking it on a Set (usually a single check).
Structs for some reason aren't really allowed to be compared using Boolean logic, thus it's hard to even search an array of Structs for an item or the item's index. So for instance, if you wanted to search an array of Structs for a specific struct and then delete it:
First, you'd need to go to your struct file and give it a protocol which allows the struct to be used in boolean logic, so...
struct Persons: Equatable {
struct properties...
struct init....
}
Then you could search within the array using array methods such as...
firstIndex(of: theStructYourLookingFor)
lastIndex(of: _)
or any other boolean-related array methods.
This could be newish as of Swift 4.0 and greater-- April 2019

SWIFT append data to dictionary

I'm having trouble coding an apparently simple task. I want to add new client profile data to a client profile dictionary (clientDatabase) but keep getting errors - can't seem to append - error: value of type '(String: clientProfile)' has no member 'append' (see error at bottom of code)
Any insights you can provide are greatly appreciated.
Thanks,
Bill
//: Playground - noun: a place where people can play
import UIKit
import Foundation
/*
code copied from B-C Dev Database - structs but simplified with fewer variables
goal is to getappend new client to work.
*/
/*
Globals: these go in main.swift file
*/
struct clientProfile {
var firstName: String = ""
var lastName: String = ""
var group: Int = 0
}
//var clientDatabase : NSMutableDictionary! = [String:clientProfile]()
var clientDatabase:[String:clientProfile]
/* sample data template: phone is key, sub array is;
(firstName: "", lastName: "",pilatesGroup: )
*/
clientDatabase = [
"1234567": clientProfile(firstName: "Sally", lastName: "Sillious", group: 3),
"2345678": clientProfile(firstName: "Sue", lastName: "Parker",group: 8),
"3456789": clientProfile(firstName: "Bob", lastName: "Parker", group: 2),
"5678901": clientProfile(firstName: "Jim", lastName: "Beam", group: 12)
]
clientDatabase.count
// so far so good
/*
add new client
declare local variables in scene swift files where used
*/
var firstName: String = ""
var phone:String = ""
var newPhone: String = ""
var newFirstName: String = ""
var newLastName: String = ""
var newGroup: Int = 0
// define struct using these input variables for values but with same keys as (global) clientDatabase
struct newClientProfile {
var firstName: String = newFirstName
var lastName: String = newLastName
var group: Int = newGroup
}
// put newClientProfile into newClientDictionary
var newClientDatabase:Dictionary = [String:newClientProfile]()
// input values from scene - UITextFields
newPhone = "4567890"
newFirstName = "Super"
newLastName = "Dave"
newGroup = 14
// test that all values are where they should be
clientDatabase
clientDatabase.count
newClientDatabase = [newPhone:newClientProfile()]
newClientDatabase.count
// ok so far
//the following line returns an error
clientDatabase.append(newClientDatabase)
// can't seem to append - error value of type '(String: clientProfile)' has no member 'append'
Two things. First of all clientDatabase is a dictionary which doesn't have append, instead you'll have to iterate through the other dictionary and insert its elements into clientDatabase.
The other issue is that clientDatabase and newClientDatabase aren't the same type. The first one is [String : clientProfile] and the second is [String : newClientProfile]. You'll have to convert the values from one type to the other to combine the dictionaries.
Looking deeper into the code there some misunderstandings about the language. For example:
struct newClientProfile {
var firstName: String = newFirstName
var lastName: String = newLastName
var group: Int = newGroup
}
// put newClientProfile into newClientDictionary
var newClientDatabase:Dictionary = [String:newClientProfile]()
You're creating a struct just for the purpose of containing a single set of values when you already have clientProfile. Instead you could do:
var newClientProfile = clientProfile(firstName: newFirstName, lastName: newLastName, group: newGroup)
This will create a variable which is an instance of clientProfile and stores the information you want. However, you have the other variables defined as empty values.
Here's a cleaned up version of your code, take a look at it and let me know if you have any questions.
struct ClientProfile { // Convention is to use initial caps for enum, struct, class
let firstName: String
let lastName: String
let group: Int
}
var clientDatabase = [
"1234567": ClientProfile(firstName: "Sally", lastName: "Sillious", group: 3),
"2345678": ClientProfile(firstName: "Sue", lastName: "Parker",group: 8),
"3456789": ClientProfile(firstName: "Bob", lastName: "Parker", group: 2),
"5678901": ClientProfile(firstName: "Jim", lastName: "Beam", group: 12)
]
// input values from scene - UITextFields
let newPhone = "4567890"
let newFirstName = "Super"
let newLastName = "Dave"
let newGroup = 14
// define struct using these input variables for values but with same keys as (global) clientDatabase
let newClientProfile = ClientProfile(firstName: newFirstName, lastName: newLastName, group: newGroup)
let newClientDatabase = [newPhone:newClientProfile]
for (phone,client) in newClientDatabase {
clientDatabase[phone] = client
}

Delcare and access multidimensional array in swift

I would like to declare an array which should look like this:
myArray
->Car (Array)
- Ford
- Audi
- ...
->Color (Array)
- Red
- Green
- ...
->Wheels (Simple String, eg "4")
->Roof (Simple String, eg, "NO")
Im stuck in space to delare and access this type of array. Thank you for any help!
I think creating custom objects (with classes or structs) would be better suited for you, like this for example:
class Car {
var brand: String?
var color: String?
var wheels: Int?
init(brand: String, color: String, wheels: Int = 4) {
self.brand = brand
self.color = color
self.wheels = wheels
}
}
let myAudi = Car(brand: "Audi", color: "red")
let myFord = Car(brand: "Ford", color: "green", wheels: 3)
var myCars = [Car]()
myCars.append(myAudi)
myCars.append(myFord)
for aCar in myCars {
println("My \(aCar.brand!) is \(aCar.color!) and has \(aCar.wheels!) wheels.")
}
Result:
My Audi is red and has 4 wheels.
My Ford is green and has 3 wheels.
Note: have a look at this, it will help.
you can create an array with each elements as an array. For example
var tempArr : [[String]] = [["Ford","Audi"],["Red","Green"],["4"],["NO"]]
based on your question i guess you need a dictionary and not an array
var tempDict : [String : [String]] = ["Car" : ["Ford","Audi"],.....]