How call method to filter by year - swift

I'm making easy example.
I'm trying to filter movies by year using func: filterByYear.
I have an error in one line on the bottom:
'var filterYear = MovieArchive.filterByYear(1980)' Compiler info is: 'Type 'MovieArchive' has no member filterByYear’
import UIKit
class Movie {
let title: String
let director: String
let releaseYear: Int
init(title: String, director: String, releaseYear: Int){
self.title = title
self.director = director
self.releaseYear = releaseYear
}
}
class MovieArchive{
var movies : [Movie]
init(movies:[Movie]){
self.movies = movies
func filterByYear(year:Int) -> [Movie]{
var filteredArray = [Movie]()
for movie in movies{
if movie.releaseYear == year {
filteredArray.append(movie)
}
}
return filteredArray
}
}
}
var newMovie1 = Movie(title: "IT", director: "S.S", releaseYear: 1980)
var newMovie2 = Movie(title: "PP", director: "O.N", releaseYear: 2003)
var moviesArray = [newMovie1, newMovie2]
var myArchive = MovieArchive(movies: moviesArray)
var filterYear = MovieArchive.filterByYear(1980)
var firstMovie = filterYear[0]
print(firstMovie)

You are calling the method on the class itself, not an instance of it. You would have to create an instance of the class like this:
let myMovieArchive = MovieArchive(movies: [Some Array])
and then call the method on the instance.
print(myMovieArchive.filterByYear)
So in your case, call it on myArchive, not MovieArchive.

Even though the question has been answered, you can simplify your filterByYear method to make it a bit more Swifty:
func filterByYear(year:Int) -> [Movie]
{
return movies.filter({ $0.releaseYear == year })
}

Related

Inner filtering of array doesn't filter swift

I am trying to filter an array of structs that has array. Below are the data structures I am using. I want the inner array filtered also but it doesn't work
var objects = [SomeObject]() //array of objects
var filteredObject = [SomeObject]() //filtered array
var isSearching = false
struct SomeObject {
var sectionName: String
var sectionObjects : [History]
}
struct History {
var firstName: String
var lastName: Int
}
func searchBar(_ text: String) {
filteredObject = objects.filter({ (obj: SomeObject) -> Bool in
return obj.sectionObjects.filter { $0.firstName.contains(text.lowercased())}.isEmpty
})
print("====", filteredObject, "fill===")
}
let history = History(firstName: "John", lastName: 1)
let anotherHistroy = History(firstName: "Dee", lastName: 2)
let historyArray = [history, anotherHistroy]
let newObject = SomeObject(sectionName: "Section 1", sectionObjects: historyArray)
objects.append(newObject)
searchBar("Jo") // printing of filtered object should not have another history in it's sectionObjects
You might be looking for something like this:
func searchBar(_ text: String) {
filteredObject = []
for var ob in objects {
ob.sectionObjects = ob.sectionObjects.filter {
$0.firstName.contains(text)
}
if !ob.sectionObjects.isEmpty {
filteredObject.append(ob)
}
}
print("====", filteredObject, "fill===")
}
Could perhaps be done more elegantly with reduce(into:), but on the whole it is best to start simply by saying exactly what you mean. You can tweak as desired to take account of case sensitivity.

Array of structs: UserDefaults, how to use?

I've already check all of those topics:
How to save an array of custom struct to NSUserDefault with swift?
How to save struct to NSUserDefaults in Swift 2.0
STRUCT Array To UserDefaults
I have a struct containing some Strings and an other struct: MySection.
struct MySection {
var name: String = ""
var values: [MyRow] = []
}
And there is MyRow which is store in MySection.values
struct MyRow {
var value: String = ""
var quantity: String = ""
var quantityType: String = ""
var done: String = ""
}
Two arrays for use it
var arraySection: [MySection] = []
var arrayRow: [MyRow] = []
And in my application, I add dynamically some values in those arrays.
There is the delegate method for get datas from my second ViewController
func returnInfos(newItem: [MyRow], sectionPick: String) {
arrayRow.append(MyRow())
arrayRow[arrayRow.count - 1] = newItem[0]
manageSection(item: sectionPick)
listTableView.reloadData()
}
And there is the manageSection function.
func manageSection(item: String) {
var i = 0
for _ in arraySection {
if arraySection[i].name == item {
arraySection.insert(MySection(), at: i + 1)
arraySection[i + 1].values = [arrayRow[arrayRow.count - 1]]
return
}
i += 1
}
arraySection.append(MySection())
arraySection[arraySection.count - 1].name = item
arraySection[arraySection.count - 1].values = [arrayRow[arrayRow.count - 1]]
}
My need is to store datas of the two arrays in UserDefaults (or CoreData maybe??) and use these datas when the user going back to the application.
I don't know how to do it, I've already try methods from the 3 topics but I'm not even doing a good job.
How can I do it?
Thanks guys!
Since both types contain only property list compliant types a suitable solution is to add code to convert each type to a property list compliant object and vice versa.
struct MySection {
var name: String
var values = [MyRow]()
init(name : String, values : [MyRow] = []) {
self.name = name
self.values = values
}
init(propertyList: [String: Any]) {
self.name = propertyList["name"] as! String
self.values = (propertyList["values"] as! [[String:String]]).map{ MyRow(propertyList: $0) }
}
var propertyListRepresentation : [String: Any] {
return ["name" : name, "values" : values.map { $0.propertyListRepresentation }]
}
}
struct MyRow {
var value: String
var quantity: String
var quantityType: String
var done: String
init(value : String, quantity: String, quantityType: String, done: String) {
self.value = value
self.quantity = quantity
self.quantityType = quantityType
self.done = done
}
init(propertyList: [String:String]) {
self.value = propertyList["value"]!
self.quantity = propertyList["quantity"]!
self.quantityType = propertyList["quantityType"]!
self.done = propertyList["done"]!
}
var propertyListRepresentation : [String: Any] {
return ["value" : value, "quantity" : quantity, "quantityType" : quantityType, "done" : done ]
}
}
After creating a few objects
let row1 = MyRow(value: "Foo", quantity: "10", quantityType: "Foo", done: "Yes")
let row2 = MyRow(value: "Bar", quantity: "10", quantityType: "Bar", done: "No")
let section = MySection(name: "Baz", values: [row1, row2])
call propertyListRepresentation to get a dictionary ([String:Any]) which can be saved to User Defaults.
let propertyList = section.propertyListRepresentation
Recreation of the section is quite easy, too
let newSection = MySection(propertyList: propertyList)
Edit
Use the propertyList initializer only if you get data from UserDefaults in all other cases use the other initializer.
For example replace
#IBAction func addButtonPressed(_ sender: Any) {
newProducts.append(MyRow(propertyList: ["":""]))
newProducts[newProducts.count - 1].value = nameTextField.text!
newProducts[newProducts.count - 1].quantity = quantityTextField.text!
newProducts[newProducts.count - 1].quantityType = type
newProducts[newProducts.count - 1].done = "No"
delegate?.returnInfos(newItem: newProducts, sectionPick: typePick)
navigationController?.popViewController(animated: true)
}
with
#IBAction func addButtonPressed(_ sender: Any) {
let row = MyRow(value: nameTextField.text!,
quantity: quantityTextField.text!,
quantityType: type,
done: "No")
newProducts.append(row)
delegate?.returnInfos(newItem: newProducts, sectionPick: typePick)
navigationController?.popViewController(animated: true)
}
and replace
func returnInfos(newItem: [MyRow], sectionPick: String) {
arrayRow.append(MyRow(propertyList: ["":""]))
arrayRow[arrayRow.count - 1] = newItem[0]
manageSection(item: sectionPick)
listTableView.reloadData()
}
with
func returnInfos(newItem: [MyRow], sectionPick: String) {
arrayRow.append(newItem[0])
manageSection(item: sectionPick)
listTableView.reloadData()
}
Basically first create the object, then append it to the array. The other way round is very cumbersome.

Swift: The proper way to initialize model class with a lot of properties

How do you initialize your classes/structs with a lot of properties?
This question could probably be asked without Swift context but Swift brings a flavour to it, so I add Swift tag in headline and tags.
Let's say you have a User class with 20 properties. Most of them should not be nil or empty. Let's assume these properties are not interdependent. Let's assume that 33% of it should be constant (let) by the logic of the class. Let's assume that at least 65% of them do not have meaningful default values. How would you design this class and initialize an instance of it?
So far I have few thoughts but none of it seems to be completely satisfactory to me:
put all of the properties linearly in the class and make huge init method:
class User {
// there is 20 properties like that
let id : String
let username : String
let email : String
...
var lastLoginDate : Date
var lastPlayDate : Date
// then HUUUUGE init
init(id: String,
username: String,
...
lastPlayDate: Date) {
}
}
try to group properties into sub types and deal with smaller inits separately
class User {
struct ID {
let id : String
let username : String
let email : String
}
struct Activity {
var lastLoginDate : Date
var lastPlayDate : Date
}
let id : ID
...
var lastActivity : Activity
// then not so huge init
init(id: ID,
...
lastActivity: Activity) {
}
}
another solution is to break requirements a bit: either declare some of the properties optional and set values after init or declare dummy default values and set normal values after init, which conceptually seems to be the same
class User {
// there is 20 properties like that
let id : String
let username : String
let email : String
...
var lastLoginDate : Date?
var lastPlayDate : Date?
// then not so huge init
init(id: String,
username: String,
email: String) {
}
}
// In other code
var user = User(id: "1", username: "user", email: "user#example.com"
user.lastLoginDate = Date()
Is there a nice paradigm/pattern how to deal with such situations?
You can try the builder pattern.
Example
class DeathStarBuilder {
var x: Double?
var y: Double?
var z: Double?
typealias BuilderClosure = (DeathStarBuilder) -> ()
init(buildClosure: BuilderClosure) {
buildClosure(self)
}
}
struct DeathStar : CustomStringConvertible {
let x: Double
let y: Double
let z: Double
init?(builder: DeathStarBuilder) {
if let x = builder.x, let y = builder.y, let z = builder.z {
self.x = x
self.y = y
self.z = z
} else {
return nil
}
}
var description:String {
return "Death Star at (x:\(x) y:\(y) z:\(z))"
}
}
let empire = DeathStarBuilder { builder in
builder.x = 0.1
builder.y = 0.2
builder.z = 0.3
}
let deathStar = DeathStar(builder:empire)
Example taken from here: https://github.com/ochococo/Design-Patterns-In-Swift
If you are looking for a bit more Java like solution, you can try something like this.
Alternative Example
final class NutritionFacts {
private let servingSize: Int
private let servings: Int
private let calories: Int
private let fat: Int
private let sodium: Int
private let carbs: Int
init(builder: Builder) {
servingSize = builder.servingSize
servings = builder.servings
calories = builder.calories
fat = builder.fat
sodium = builder.sodium
carbs = builder.carbs
}
class Builder {
let servingSize: Int
let servings: Int
private(set) var calories = 0
private(set) var fat = 0
private(set) var carbs = 0
private(set) var sodium = 0
init(servingSize: Int, servings: Int) {
self.servingSize = servingSize
self.servings = servings
}
func calories(value: Int) -> Builder {
calories = value
return self
}
func fat(value: Int) -> Builder {
fat = value
return self
}
func carbs(value: Int) -> Builder {
carbs = value
return self
}
func sodium(value: Int) -> Builder {
sodium = value
return self
}
func build() -> NutritionFacts {
return NutritionFacts(builder: self)
}
}
}
let facts = NutritionFacts.Builder(servingSize: 10, servings: 1)
.calories(value: 20)
.carbs(value: 2)
.fat(value: 5)
.build()
Example taken from: http://ctarda.com/2017/09/elegant-swift-default-parameters-vs-the-builder-pattern

How to create a shared part between two object

List and User are 2 classes. List contains title and a set of movies someone may likes. The class User has a AddList(_: List) method. It add new movie list to object of User type
class List {
var title : String = "General"
var movies = [String]()
init(title: String){
self.title = title
}
init(title: String, movies: [String]){
self.title = title
self.movies = movies
}
}
class User {
private let userID: Int
let userName: String
private var movieList = [String: [String]]() // the list is a Dictionaly, the value of the dictionalry is a string array
init(userID: Int, userName: String){
self.userID = userID
self.userName = userName
}
func AddList(newMovieList: List) -> Dictionary<String, [String]> {
self.movieList.updateValue(newMovieList.movies, forKey: newMovieList.title)
return self.movieList
}
}
3 movie list had beend inilizied as follow
var actionMovieJhon = List(title: "Action", movies: ["The Equalizer", "Mad Max: Fury Road", "Star Wars"])
var actionMovieAnna = List(title: "Action", movies: ["Batman v Superman: Dawn of Justice", "American Sniper", "The Martia"])
var shareMovie = List(title: "ShareAction", movies: ["MovieA", "MovieB", "MovieC"])
And then 2 User objdects Jhon and Anna with ther own movie list was created
var Jhon = User(userID: 1001, userName: "Jhon")
var Anna = User(userID: 1002, userName: "Anna")
Jhon.AddList(actionMovieJhon)
Anna.AddList(actionMovieAnna)
Question: How can I add the shared movie list (sharMovie) into the dictionalry of Jhon and Anna, so that anything changed in the shared list of Jhon will get updated in the side of Anna and vice versa.
In anther word, the "dictionary" of Jhon and Anna has their own part and shared part. But the shared part is referenced to the same list object
More Question: I had feelings that the method self.movieList.updateValue is not update by reference
Thanks a lot for your help
The code is based on the sorece code provided by "Swift Apprentice, Beginning programming with Swift 2.2", chapter 14: Classes
import Foundation
class List {
let name: String
var movies: [String] = []
init(name: String){
self.name = name
}
func printList() {
print("Movie List: \(name)")
for movie in movies{
print(movie)
}
print("\n")
}
}
class User {
var lists: [String: List] = [:]//****
func addList(list: List){
lists[list.name] = list
}
func listForName(name: String)-> List? {
print(lists[name]?.movies)
return lists[name]
}
}
//create user
let jane = User()
let john = User()
let people = User()
//create list
var johnActionMovie = List(name: "ActionJhon")
var janeActionMovie = List(name: "ActionJane")
var sharedMovie = List(name: "SharedMovie")
var publicIntrest = sharedMovie
//call addList(_:) method on user object
jane.addList(janeActionMovie)
john.addList(johnActionMovie)
jane.addList(sharedMovie)
john.addList(sharedMovie)
people.addList(publicIntrest)
//private part appending
jane.lists["ActionJane"]?.movies.append("janeA")
jane.lists["ActionJane"]?.movies.append("janeB")
jane.lists["ActionJane"]?.movies.append("janeC")
john.lists["ActionJhon"]?.movies.append("johnA")
john.lists["ActionJhon"]?.movies.append("johnB")
john.lists["ActionJhon"]?.movies.append("johnB")
//Shared part appending
john.lists["SharedMovie"]?.movies.append("shareA")
jane.lists["SharedMovie"]?.movies.append("shareB")
people.lists["SharedMovie"]?.movies.append("shareC")
//print out the result
john.listForName("ActionJhon")
john.listForName("SharedMovie")
jane.listForName("ActionJane")
jane.listForName("SharedMovie")
people.listForName("SharedMovie")
//further check
print(jane.lists["ShareMovie"] === people.lists["ShareMovie"])
print(jane.lists["ActionJane"] === john.lists["ActionList"])

Error:Missing argument for parameter 'makePetMakeNoise' in call

Hi I am doing some Swift coding and I cant figure out how to get rid of this error:
Missing argument for parameter 'makePetMakeNoise' in call.
Could you help me fix this error?
import Foundation
import UIKit
class Human {
static var numCreated:Int = 4
var name:String = ""
var pet:Pet
init(name:String,pet:Pet){
self.name = name
self.pet = pet
Human.numCreated++
}
func makePetMakeNoise(){
var randomNumber = arc4random_uniform(9)
self.pet.makeNoise(randomNumber) //Missing argument for parameter 'makePetMakeNoise' in call
}
func feedPet(){
self.pet.eat
}
static func populationCount(){
println("Total population count is \(Human.numCreated)")
}
}
class Pet {
var name:String = ""
var noise:String = ""
var canMakeNoise:Bool = true
init(name:String,noise:String,canMakeNoise:Bool){
self.name = name
self.noise = noise
self.canMakeNoise = canMakeNoise
}
func makeNoise(canMakeNoise: Int, makePetMakeNoise: Int){
if self.canMakeNoise {
for _ in 1...5{
println("\(self.name) \(self.noise)")
}
}else {
println("\(self.name) *remains silent*")
}
}
func eat(){
println("\(name) is eating")
}
class Dog:Pet{
}
class Cat:Pet{
override func eat {
super.eat()
println("I'm still hungry, meow")
}
}
}
//Pets
var Tobie = Pet(name: "Tobie", noise: "Bark", canMakeNoise: true)
var Bud = Pet(name: "Bud", noise: "Bark", canMakeNoise: false)
var Ginger = Pet(name: "Ginger", noise: "bark", canMakeNoise: false)
var Curry = Pet(name: "Curry", noise: "Bark", canMakeNoise: true)
//Humans
var Sam = Human(name: "Sam", pet: Tobie)
var Mark = Human(name: "Mark", pet: Bud)
var Spencer = Human(name: "Spencer", pet: Ginger)
var Jessie = Human(name: "Jessie", pet: Curry)
let Humans = [Sam, Mark, Spencer, Jessie]
for Human in Humans {
println("\(Humans) \(feedPet) \(makePetMakeNoise)") //Use of unresolved identifier 'feedPet and makePetMakeNoise'\\
}
`
Your makeNoise method defined for Pet takes two parameters. Problem is that when you call it in the line that gives you the error, you pass it just one parameter.
Possible solution is:
Change the Pet's method as follow:
func makeNoise(makePetMakeNoise: Int){
if self.canMakeNoise {
for _ in 1...5{
println("\(self.name) \(self.noise)")
}
}else {
println("\(self.name) *remains silent*")
}
}
Update the code that gives you the error in Human like this:
func makePetMakeNoise(){
var randomNumber = Int(arc4random_uniform(9))
self.pet.makeNoise(randomNumber)
}
Notice I convert random to Int since this is the expected parameter type.
Please consider I'm just guessing a possible modification since I don't know what your classes are intended for ... maybe just a playground.
Other solution is effectively passing two parameters when you call the pet's method from human.
Hope this helps
Your function func makeNoise(canMakeNoise: Int, makePetMakeNoise: Int) expects two arguments, but your are only passing the one randonNumber in your call self.pet.makeNoise(randomNumber). It should look something like this: self.pet.makeNoise(randomNumber, someOtherNumber)