Hi I have a task to implement the Fleet protocol which has two functions:
addNewCar - adds a new car object to the Fleet.
- Parameter car: car to add to the Fleet
- Returns: false if the car with same id already exists in the Fleet, true – otherwise.
deleteCar - Deletes the car with the specified id from the Fleet.
- Returns: true if the car with same id existed in the Fleet, false – otherwise.
listCarsByModel - returns 10 car models containing the specified string.
If there are several cars with the same model, brand's name is added to car's model in the format "brand - car",
otherwise returns simply "car".
listCarsByBrand - returns 10 car models whose brand contains the specified string,
result is ordered by brand.
struct Car {
let id: String; // unique identifier
let model: String;
let brand: String;
}
protocol Fleet {
func addNewCar(car: Car) -> Bool
func deleteCar(id: String) -> Bool
func listCarsByModel(searchString: String) -> Set<String>
func listCarsByBrand(searchString: String) -> [String]
}
class FleetImpl: Fleet {
var cars: [Car] = []
func addNewCar(car: Car) -> Bool {
if let i = cars.firstIndex(where: { $0.id == car.id }) {
print(i)
return false
} else {
cars.append(car)
print(car)
return true
}
}
func deleteCar(id: String) -> Bool {
return true
}
func listCarsByModel(searchString: String) -> Set<String> {
}
func listCarsByBrand(searchString: String) -> [String] {
}
}
I've used method firstIndex(where:). But the function adds new car to the array with existing id. (i.e. two or more cars with the same id)
How can I access that 'id' property of Car struct in order to manipulate that data?
Please help me out
This is the test code:
func test(fleet: Fleet) {
assert(!fleet.deleteCar(id: "1"))
assert(fleet.addNewCar(car: Car(id: "1", model: "1", brand: "Lex")))
assert(!fleet.addNewCar(car: Car(id: "1", model: "any name because we check id only", brand: "any brand")))
assert(fleet.deleteCar(id: "1"))
assert(fleet.addNewCar(car: Car(id: "3", model: "Some Car3", brand: "Some Brand2")))
assert(fleet.addNewCar(car: Car(id: "4", model: "Some Car1", brand: "Some Brand3")))
var byModels: Set<String> = fleet.listCarsByModels(searchString: "Car")
assert(byModels.count == 10)
byModels = fleet.listCarsByModels(searchString: "Some Car")
assert(byModels.count == 4)
assert(byModels.contains("Some Brand3 - Some Car1"))
assert(byModels.contains("Some Car2"))
assert(byModels.contains("Some Car3"))
assert(!byModels.contains("Some Car1"))
assert(byModels.contains("Some Brand1 - Some Car1"))
var byBrand: [String] = fleet.listCarsByBrand(searchString: "Brand")
assert(byBrand.count == 10)
byBrand = fleet.listCarsByBrand(searchString: "Some Brand")
assert(byBrand.count == 4)
assert(byBrand[0] == "Some Car1")
assert(byBrand[1] == "Some Car2" || byBrand[1] == "Some Car3")
assert(byBrand[2] == "Some Car2" || byBrand[2] == "Some Car3")
assert(byBrand[3] == "Some Car1")
}
test(fleet: FleetImpl())
Maybe a set instead of an array would work better in this case as we're dealing with unique elements. Also note that rather than using firstIndex(where) I'm using first(where) which will return the car rather than index.
import UIKit
struct Car: Hashable {
let id: String
let model: String
let brand: String
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
protocol Fleet {
func addNewCar(car: Car) -> Bool
func deleteCar(id: String) -> Bool
func listCarsByModel(searchString: String) -> [String]
func listCarsByBrand(searchString: String) -> [String]
}
class FleetImpl: Fleet {
var cars = Set<Car>()
func addNewCar(car: Car) -> Bool {
guard !cars.contains(where: { $0.id == car.id }) else { return false }
cars.insert(car)
return true
}
func deleteCar(id: String) -> Bool {
guard let car = cars.first(where: { $0.id == id }) else { return false }
cars.remove(car)
return true
}
func listCarsByModel(searchString: String) -> [String] {
let searchedCars = cars.filter { $0.model.lowercased().contains(searchString.lowercased()) }.sorted { $0.brand < $1.brand }
var formattedModels = [String]()
searchedCars.forEach { car in
if let car = searchedCars.first(where: { $0.model == car.model && $0.id != car.id }) {
let carName = [car.model, car.brand].joined(separator: " - ")
formattedModels.append(carName)
} else {
formattedModels.append(car.model)
}
}
return Array(formattedModels.prefix(10))
}
func listCarsByBrand(searchString: String) -> [String] {
let searchedBrands = cars.filter { $0.brand.lowercased().contains(searchString.lowercased()) }.sorted { $0.brand < $1.brand }.map { $0.model }
return Array(searchedBrands.prefix(10))
}
}
Related
My code...
class Inbox {
var user = "name"
var pmsg = "label"
var match = ""
var resim = "photo"
var userID = ""
var distance = ""
var updated = ""
var isAttendingNow = ""
var isAttendingNowText = ""
init(user : String, pmsg: String, match: String, resim: String, userID : String, distance: String, updated: String, isAttendingNow: String, isAttendingNowText: String) {
self.user = user
self.pmsg = pmsg
self.match = match
self.resim = resim
self.userID = userID
self.distance = distance
self.updated = updated
self.isAttendingNow = isAttendingNow
self.isAttendingNowText = isAttendingNowText
}
}
var arrayOfRels: [Inbox] = [Inbox]()
My goal is to remove duplicate items for userID key.
How can I achieve that?
You could use a set to figure out which useIDs are unique:
func filteredRels(source [Inbox]) -> [Inbox] {
var keys: Set<String> = []
return source.filter {
if keys.contains($0.userID) {
return false
} else {
keys.insert($0.userID)
return true
}
}
}
(Banged out in the editor, so it might need some minor cleanup.)
Use Hashable
class RRR : Hashable {
var hashValue: Int = 0
static func == (lhs: RRR, rhs: RRR) -> Bool {
// in your case set only userID
return lhs.name == rhs.name && lhs.age == rhs.age
}
var name:String
var age:Int
init(name:String,age:Int) {
self.name = name
self.age = age
}
}
//
let arr = [RRR(name: "qqq", age: 12) ,RRR(name: "qqq", age: 12) , RRR(name: "hhhh", age: 12) , RRR(name: "ppppp", age: 12) ]
let set = Array(Set(arr))
print(set) // [ RRR(name: "qqq", age: 12) , RRR(name: "hhhh", age: 12) , RRR(name: "ppppp", age: 12)]
Checkout this:
extension Sequence where Iterator.Element: Hashable {
func uniqueOrdered() -> [Iterator.Element] {
return reduce([Iterator.Element]()) { $0.contains($1) ? $0 : $0 + [$1] }
}
}
class Inbox: Hashable {
...
...
static func == (lhs: User, rhs: User) -> Bool {
return lhs.userID == rhs.userID
}
}
arrayOfRels.uniqueOrdered()
You could do this in a couple of lines using a set:
var unique = Set<String>()
arrayOfRels = arrayOfRels.filter{unique.insert($0.userID).inserted}
I am facing an issue where I am unable to keep existing relationships after calling add(_, update: true) function.
I wrote a TaskSync class that is responsible for creating/updating Task objects:
class TaskSync: ISync {
typealias Model = Task
func sync(model: Task) {
let realm = try! Realm()
let inWrite = realm.isInWriteTransaction
if !inWrite {
realm.beginWrite()
}
let _task = realm.object(ofType: Task.self, forPrimaryKey: model.id)
// Persist matches as they are not getting fetched with the task
if let _task = _task {
print("matches: \(_task.matches.count)")
model.matches = _task.matches
}
realm.add(model, update: true)
if _task == nil {
var user = realm.object(ofType: User.self, forPrimaryKey: model.getUser().id)
if (user == nil) {
user = model.getUser()
realm.add(user!, update: true)
}
user!.tasks.append(model)
}
if !inWrite {
try! realm.commitWrite()
}
}
func sync(models: List<Task>) {
let realm = try! Realm()
try! realm.write {
models.forEach { task in
sync(model: task)
}
}
}
}
When a model is to be synced, I check if it already exists in the Realm and if so, I fetch it and try to include the matches property as this one is not included in the model.
Right before the call realm.add(model, update: true), model contains list of matches, however right after the realm.add is executed, the matches list is empty.
Here are the two models:
class Task: Object, ElementPreloadable, ElementImagePreloadable, ItemSectionable {
dynamic var id: Int = 0
dynamic var title: String = ""
dynamic var desc: String = ""
dynamic var price: Float = 0.0
dynamic var calculatedPrice: Float = 0.0
dynamic var location: String = ""
dynamic var duration: Int = 0
dynamic var date: String = ""
dynamic var category: Category?
dynamic var currency: Currency?
dynamic var longitude: Double = 0.0
dynamic var latitude: Double = 0.0
dynamic var state: Int = 0
dynamic var userId: Int = 0
// Existing images
var imagesExisting = List<URLImage>()
// New images
var imagesNew = List<Image>()
// Images deleted
var imagesDeleted = List<URLImage>()
private let users = LinkingObjects(fromType: User.self, property: "tasks")
var user: User?
var matches = List<Match>()
dynamic var notification: Notification?
override static func ignoredProperties() -> [String] {
return ["imagesExisting", "imagesNew", "imagesDeleted", "user", "tmpUser"]
}
override static func primaryKey() -> String? {
return "id"
}
func getImageMain() -> URLImage? {
for image in imagesExisting {
if image.main {
return image
}
}
return imagesExisting.first
}
func getSection() -> Int {
return state
}
func getSectionFieldName() -> String? {
return "state"
}
func getId() -> Int {
return id
}
func getURL() -> URL? {
if let image = getImageMain() {
return image.getResizedURL()
}
return nil
}
func getState() -> TaskOwnState {
return TaskOwnState(rawValue: state)!
}
func getUser() -> User {
return (user != nil ? user : users.first)!
}
}
class Match: Object, ElementPreloadable, ElementImagePreloadable, ItemSectionable {
dynamic var id: Int = 0
dynamic var state: Int = -1
dynamic var priorityOwnRaw: Int = 0
dynamic var priorityOtherRaw: Int = 0
dynamic var user: User!
var messages = List<Message>()
private let tasks = LinkingObjects(fromType: Task.self, property: "matches")
var task: Task?
dynamic var notification: Notification?
override static func primaryKey() -> String? {
return "id"
}
override static func ignoredProperties() -> [String] {
return ["task"]
}
func getId() -> Int {
return id
}
func getSection() -> Int {
return 0
}
func getURL() -> URL? {
if let image = user.getImageMain() {
return image.getResizedURL()
}
return nil
}
func getPriorityOwn() -> PriorityType {
if priorityOwnRaw == PriorityType.normal.rawValue {
return PriorityType.normal
}
else {
return PriorityType.favorite
}
}
func getPriorityOther() -> PriorityType {
if priorityOtherRaw == PriorityType.normal.rawValue {
return PriorityType.normal
}
else {
return PriorityType.favorite
}
}
func getSectionFieldName() -> String? {
return nil
}
func getTask() -> Task {
return (task != nil ? task : tasks.first)!
}
}
I spent hours trying to figure out why I am unable to keep the matches relationship when updating the task. Every advice will be highly appreciated!
This question was also asked upon Realm's GitHub issue tracker. For posterity, here is the solution.
List properties should always be declared as let properties, as assigning to them does not do anything useful. The correct way to copy all objects from one List to another is model.tasks.append(objectsIn: _user.tasks).
I have a class called Person:
class Person{
let name: String?
let areas: [Area]
}
And Area consisting of the following:
class Area{
let id: String
let name: String
}
I've created a "people" array and what I want to do is check the "people" array if area.name = "Brooklyn". If it doesn't remove that person from the "people" array.
Here is what I have tried but it doesn't work:
var people = [Person]()
for s in people{
for a in s.preferredArea{
if a.areaName != "Brooklyn"{
let index = people.indexOf(s)
people.removeAtIndex(index!)
}
}
}
Can anyone point me into the right direction please?
To use indexOf, you'll have to make Person conforming the protocol Equatable:
class Person: Equatable {
let name: String
var area: [Area]
init(name: String) {
self.name = name
self.area = []
}
}
func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.name == rhs.name &&
lhs.area == rhs.area // You'll have to make Area equatable too
}
Having this done, your code just works fine. If you don't want to use this method, you can use:
people.enumerate().forEach {
if !$0.1.area.contains({ $0.name == "Brooklyn" }) {
people.removeAtIndex($0.0)
}
}
Or if you can use filter to create a new array:
var newPeople = people.filter { $0.area.contains { $0.name == "Brooklyn" } }
For my iOS app I have a model something like
class Person {
var Id: Int
var Name: String
init(id: Int, name: String?) {
self.Id = id
self.Name = name ?? ""
}
}
Then later on in my ViewController when I load data from the server I add some people to an array
class ViewController: UIViewController {
var people:[Person] = []
override func viewDidLoad() {
self.loadPeople()
}
func loadPeople() {
// This data will be coming from a server request
// so is just sample. It could have users which
// already exist in the people array
self.people.append(Person(id: "1", name: "Josh"))
self.people.append(Person(id: "2", name: "Ben"))
self.people.append(Person(id: "3", name: "Adam"))
}
}
What I am now trying todo is turn the people array into a Set<Person> so it will not add duplicates. Is this possible to do or do I need to change my logic?
To make set of Person you need to make it conform to Equatable and Hashable protocols:
class Person: Equatable, Hashable {
var Id: Int
var Name: String
init(id: Int, name: String?) {
self.Id = id
self.Name = name ?? ""
}
var hashValue: Int {
get {
return Id.hashValue << 15 + Name.hashValue
}
}
}
func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.Id == rhs.Id && lhs.Name == rhs.Name
}
Then you can use set of persons like this:
var set = Set<Person>()
set.insert(Person(id: 1, name: "name"))
With Swift 2.0, Hashable and Equitable is a part of NSObject. All you need to do is to override "isEqual" and "var hash:" for the property of interest. In this case: "Id", Set will exclude Person-objects with identical Ids.
class Person: NSObject {
var Id: Int
var Name: String
init(id: Int, name: String?) {
self.Id = id
self.Name = name ?? ""
}
override var hash: Int {
return Id.hashValue
}
override func isEqual(object: AnyObject?) -> Bool {
guard let rhs = object as? Person else {
return false
}
let lhs = self
return lhs.Id == rhs.Id
}
}
func mergeArrays(){
let person1 = Person(id: 1, name: "Tom")
let person2 = Person (id: 2, name: "John")
let person3 = Person(id: 3, name: "Adam")
let downloadedPeople = [person1,person2] //[{NSObject, Id 1, Name "Tom"}, {NSObject, Id 2, Name "John"}]
let peopleStoredLocally = [person1,person3] //[{NSObject, Id 1, Name "Tom"}, {NSObject, Id 3, Name "Adam"}]
let downloadedPeopleSet = Set(downloadedPeople) //{{NSObject, Id 2, Name "John"}, {NSObject, Id 1, Name "Tom"}}
let mergedSet = downloadedPeopleSet.union(peopleStoredLocally) //{{NSObject, Id 2, Name "John"}, {NSObject, Id 3, Name "Adam"}, {NSObject, Id 1, Name "Tom"}}
let mergedArray = Array(mergedSet)//[{NSObject, Id 2, Name "John"}, {NSObject, Id 3, Name "Adam"}, {NSObject, Id 1, Name "Tom"}]
}
UPDATE
Depretation warning when using hashValue:
'Hashable.hashValue' is deprecated as a protocol requirement; conform
type 'Person' to 'Hashable' by implementing 'hash(into:)' instead
Following the object Person example, nowadays implementation would be:
class Person: Equatable, Hashable {
let id: Int
let countryId: Int
var name: String
init(id: Int, countryId: Int, name: String) {
self.id = id
self.countryId = countryId
self.name = name
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(countryId)
}
static func == (lhs: Person, rhs: Person) -> Bool {
return lhs.id == rhs.id && lhs.countryId == rhs.countryId
}
}
Note: As per documentation the components used for hashing must be the same as the components compared in your type’s == operator implementation.
Here I have a class, Player, that has a variable of type, Sport, of which can be Basketball or Soccer. I'd like to be able to declare the type of Sport in the Player declaration. Any suggestions?
class Soccer : Sport {
override var players : Int { get { return 11 } }
}
class Basketball : Sport {
override var players : Int { get { return 5 } }
}
class Sport {
var teamName: String
var players: Int { get { return 0 } }
init(teamName: String) {
self.teamName = teamName
}
}
class Player {
let sport : Sport?
init? (typeOfSport: Soccer, teamName: String) {
self.sport = Soccer(teamName: teamName)
}
init? (typeOfSport: Basketball, teamName: String) {
self.sport = Basketball(teamName: teamName)
}
}
let me = Player(typeOfSport: Soccer(), teamName: "chelsea")
let him = Player(typeOfSport: Basketball(), teamName: "wizards")
You could also use an enum for this like this:
enum Sport {
case Soccer (teamName : String)
var players: Int {
switch self{
case .Soccer: return 11
default: return 0
}
}
}
class Player {
let sport: Sport?
init? (s : Sport){
self.sport = s
}
}
Sport.Soccer (teamName: "Cambuur").players
In this case I suggest for your example is this solution, I don't know if is the better way, but in my studies about OOP, I believe it is a cool way to do your example.
protocol Sport {
func getTotalPlayers() -> Int
}
class Soccer: Sport {
func getTotalPlayers() -> Int {
return 11
}
}
class Basketball: Sport {
func getTotalPlayers() -> Int {
return 5
}
}
class Team {
private var sport: Sport
private var name: String
init(sport:Sport, name:String) {
self.sport = sport
self.name = name
}
func getTeamName() -> String {
return name
}
func getSport() -> Sport {
return sport
}
}
class Chelsea: Team {
init() {
super.init(sport: Soccer(), name: "Chelsea")
}
}
class Wizards: Team {
init() {
super.init(sport: Basketball(), name: "Wizards")
}
}
class Player {
private var team: Team
init(team: Team) {
self.team = team
}
func getTeamName() -> String {
return self.team.getTeamName()
}
func getSport() -> Sport {
return self.team.getSport()
}
}
let me = Player(team: Chelsea())
let him = Player(team: Wizards())
I found a way to do this.. If you declare the typeOfSport in the player initialization function Sport.Type and then make the Sport initialization method required like so....
class Soccer : Sport {
override var players : Int { get { return 11 } }
}
class Basketball : Sport {
override var players : Int { get { return 5 } }
}
class Sport {
var teamName: String
var players: Int { get { return 0 } }
required init(teamName: String) {
self.teamName = teamName
}
}
class Player {
let sport : Sport?
init? (typeOfSport: Sport.Type, teamName: String) {
self.sport = Soccer(teamName: teamName)
}
init? (typeOfSport: Basketball, teamName: String) {
self.sport = Basketball(teamName: teamName)
}
}
let me = Player(typeOfSport: Soccer.self, teamName: "chelsea")
let him = Player(typeOfSport: Basketball.self, teamName: "wizards")