Merge two arrays into one by key - swift

I have two Arrays. I want to use them, to create the new one. Then, the new list will be use in the List View. So how it looks like? I have two models that are the basis for lists:
Photo:
class Photo: Object, Identifiable, Decodable {
#objc dynamic var albumId: Int = 0
#objc dynamic var id: Int = 0
#objc dynamic var title: String = ""
#objc dynamic var url: String = ""
#objc dynamic var thumbnailUrl: String = ""
override static func primaryKey() -> String? {
return "id"
}
}
Album:
class Album: Object, Decodable {
#objc dynamic var id: Int = 0
#objc dynamic var userId: Int = 0
#objc dynamic var title: String = ""
override static func primaryKey() -> String? {
return "id"
}
}
The child of both is ListItem. So the vars of the ItemLists are using vars from the Photo and Album classes. I hope I explained it well. If not, please ask.
class ListItem {
var id: Int = 0 Album id
var title: String = "" Photo title
var albumTitle: String = "" Album title
var thumbnailUrl: String = "" Photo thumbnailUrl
}

You can use zip to merge the 2 arrays together, then call map to create ListItems from each element.
let list = zip(photos, albums).map { photo, album in ListItem(id: album.id, title: photo.title, albumTitle: album.title, thumbnailUrl: photo.thumbnailUrl)}

If I understand your question directly you have a one to many relationship between Album and Photo so for each Photo we should look up the correct Album and create a new ListItem
This can be done using this code
let list: [ListItem] = photoArray.compactMap { photo in
guard let album = albumArray.first(where: { $0.id == photo.id }) else {
return nil
}
return ListItem(id: album.id, title: photo.title, albumTitle: album.title, thumbnailUrl: photo.url)
}

Related

Overlapping accesses to 'but modification requires exclusive access; consider copying to a local variable'

an employee can own more than one animal, but an animal must have only one employee.
First, I add an employee. Then when adding an animal, I select the employee.
After selecting the employee, I press the save button.
I want to transfer the animal I just added to that employee.
I want to take the employee's id and add it to the employee attribute of the animal I added.
Animal Model:
struct Animal {
let id: String?
var name: String?
var sound: String?
var age: Double?
var waterConsumption: Double?
var employee: Employee?
var animalType: String?
}
Employee Model:
struct Employee {
let id: String?
var name: String?
var lastName: String?
var age: Int?
var gender: String?
var salary: Double?
var experienceYear: String?
var animals: [Animal]?
}
ZooManager:
Error is on this line: newAnimal.employee?.animals?.append(newAnimal)
Overlapping accesses to 'newAnimal.employee', but modification requires exclusive access; consider copying to a local variable
protocol ZooManagerProtocol {
var animals: [Animal] { get set }
var employees: [Employee] { get set }
func addNewAnimal(_ model: Animal)
}
class ZooManager: ZooManagerProtocol {
static var shared = ZooManager()
var animals: [Animal] = []
var employees: [Employee] = []
private init() { }
func addNewAnimal(_ model: Animal) {
var newAnimal = model
guard var employee = employees.filter({ $0.id == model.employee?.id }).first else { return }
newAnimal.employee = employee
newAnimal.employee?.animals?.append(newAnimal)
dump(newAnimal)
}
func addNewEmployee(_ model: Employee) {
employees.append(model)
}
}
The solution recommended by the compiler works
func addNewAnimal(_ model: Animal) {
var newAnimal = model
guard var employee = employees.filter({ $0.id == model.employee?.id }).first else { return }
employee.animals?.append(newAnimal)
newAnimal.employee = employee
}
I've never seen this error before, hopefully someone can add a good answer explaining the why and not just the how!
EDIT:
Here's the why https://github.com/apple/swift-evolution/blob/main/proposals/0176-enforce-exclusive-access-to-memory.md

Filtering multidimensional array in uitableview - swift

Here is my model
class BusinessProfile: NSObject {
var title: String?
var website: String?
var associatedOrganization: String?
var companyName: String?
var productList: [BusinessProfileProduct]?
}
class BusinessProfileProduct: NSObject{
var productName: Double?
var modelNumber: String?
var hsCode: String?
}
Here are my array variables in view controller.
var businessProfileArray = [BusinessProfile]()
var tempBusinessProfileArray = [BusinessProfile]()
I already have filtered businessProfileArray on the basis of companyName like below:
tempBusinessProfileArray = businessProfileArray.filter({ (BusinessProfile) -> Bool in
return (BusinessProfile.companyName!.lowercased().contains(searchText.lowercased()))
})
But I am unable to filter businessProfileArray on the basis of productName or hsCode from nested array of BusinessProfileProduct.
Note: businessProfileArray contains array of businessProfileProduct
Any help from anyone? Thanks in advance.
You can do something similar to this
func filterArr(searchText:String) -> [BusinessProfile] {
var filteredArr = [BusinessProfile]()
for profile in businessProfileArray {
var isMatched = false
if let matchedProduct = profile.productList.filter ({$0.companyName.lowercased().contains(searchText.lowercased())}).first {
isMatched = true
print(matchedProduct.companyName)
}
if isMatched {
filteredArr.append(profile)
}
}
return filteredArr
}
this will return all the profiles in which there is a match of searchText with product's companyName however it will not remove the extra products which does not match the searchText

How to save and load GKGameModelPlayer from Realm in Swift?

I am attempting to implement a GKGameModel in my application. In it, it holds variables to a few things, but for the purposes of my question I'm interested in the following two variables:
import GameplayKit
final class GameModel: NSObject, GKGameModel {
var players: [GKGameModelPlayer]?
var activePlayer: GKGameModelPlayer?
}
I do something like this to initialise the game with 3 players (not exact)
let game = GameModel.init()
game.players = [Player(),Player(),Player()] // Create 3 players
guard let firstPlayer = game.players.first else {
return
}
game.activePlayer = firstPlayer
A player class is defined as:
class Player : NSObject, GKGameModelPlayer {
var playerId: Int // GKGameModelPlayer protocol variable
let name: String
var cash: Int = 0
}
In my project I have Realm Entities and the models seperated. So there will be a PlayerEntity and a Player class.
I'm wanting to use RealmSwift to save and load the GKGameModelPlayer data, and more specifically the ability to store/re-store the active player.
I think the key here is the playerId variable; but I am not sure.
But what I'm not sure about is retrieving this information and then re-mapping it into a valid GKGameModelPlayer format
My current idea/theory is that I need to map my model to an entity class and vice-versa.
Ie:
// [REALM] Player entity
class PlayerEntity: Object {
#objc dynamic var id = UUID().uuidString
#objc dynamic var playerId: Int = 0
#objc dynamic var name: String = ""
#objc dynamic var cash: Int = 0
override static func primaryKey() -> String {
return "id"
}
}
And then I extend this class to do some "mapping":
extension PlayerEntity {
// Map model -> entity
convenience init(model: Player) {
self.init()
self.playerId = model.playerId
self.name = model.name
self.cash = model.cash
}
}
extension Player {
// Map entity -> model
convenience init(entity: PlayerEntity) {
let playerId = entity.playerId
let name = entity.name
let cash = entity.cash
self.init(id: playerId, name: name, cash: cash)
}
}
Right now, the playerId is always zero (0) because I'm not really sure how to set it.
I can save a player to realm.
The issue comes from when I try to restore the player, and I want to restore the activePlayer variable in the GameModel
Therefore, my question is:
How would I go about saving and restoring the activePlayer variable so that it continues to comply to GKGameModelPlayer?
I appreciate any assistance on this.
With thanks
While you could use those extensions, sometimes simpler is better. Here's a rough example:
class PlayerEntity: Object {
#objc dynamic var playerId: Int = 0
#objc dynamic var name: String = ""
#objc dynamic var cash: Int = 0
convenience init(withPlayer: PlayerClass) {
self.init()
self.playerId = withPlayer.playerId
self.name = withPlayer.name
self.cash = withPlayer.cash
}
func getPlayer() -> Player {
let p = Player()
p.playerId = self.playerId
p.name = self.name
p.cash = self.cash
return p
}
override static func primaryKey() -> String {
return "playerId"
}
}
to load all the players into an array... this will do it
let playerResults = realm.objects(PlayerEntity.self)
for player in playerResults {
let aPlayer = player.getPlayer()
self.playerArray.append(aPlayer)
}
Notice the removal of
#objc dynamic var id = UUID().uuidString
because it's not really being used to identify the object as a primary key.
The primary key is really
var playerId: Int // GKGameModelPlayer protocol variable
which is fine to use as long as it's unique.

How to search in TableView by Section and Row?

How to search in TableView by Section and Row ?
Get nested JSON from the site.
self.partnerBranchs = try decoder.decode([PartnerBranch].self, from: data)
Model:
struct PartnerBranch: Decodable {
let id: Int?
let address: String?
let contacts: String?
let name: String?
let logo: String?
let stocks: [Stock]
}
struct Stock: Decodable {
let id: Int?
let title: String?
let description: String?
let images: String?
}
PartnerBranch is a section
Stock is a row
How do I search for both?
Now works only on sections:
func filterContentForSearchText(_ searchText: String) {
partnerBranchsFiltered = partnerBranchs.filter({ (partnerBranch: PartnerBranch) -> Bool in
return partnerBranch.name!lowercased().contains(searchText.lowercased()
})
As I understand it I have to somehow use compactMat for stocks.
I think you need to use reduce here. From how i understand your problem, it seems that you want to show the whole section if the search term matches your section title. But if it doesn't, you still want to show the section along with the matching rows. So, you could achieve it with reduce, like this,
let filteredBranchs: [PartnerBranch] = partnerBranchs.reduce(into: []) { (result, branch) in
let partnerNameMatches = branch.name?.lowercased().contains(searchText.lowercased()) ?? false
if partnerNameMatches {
result.append(branch)
} else {
let rowMatches = branch.stocks.filter({ (stock) -> Bool in
return stock.title?.lowercased().contains(searchText.lowercased()) ?? false
})
if !rowMatches.isEmpty {
var filteredPartnerBranch = branch
filteredPartnerBranch.stocks = rowMatches
result.append(filteredPartnerBranch)
}
}
}

How to create one Realm class from several other classes?

Good afternoon everyone,
Im currently working on a task that called "Bookmark". The short description is whenever i clicked the bookmark button, app will save the article and then display it in the BookmarkVC.
I have 3 types of object called "News", "Documents" and "ITSectionResult" and my idea is to create an object called "BookmarkItem" which contain element of 3 above objects and one property called bookmarkCategory to indicate type of objects, so that i can use it to display in BookmarkVC. I just want to work on only one realm object, so any one can help me an an idea to group these guys together?. Here i would attach my draft code as below:
For News class:
class NewsArticle:Object {
dynamic var title: String?
dynamic var body:String?
dynamic var category:String?
dynamic var createTime:String?
dynamic var image:String?
dynamic var id:String?
convenience init (title:String, body:String, category:String,
image:String, id:String, createTime:String) {
self.init()
self.title = title
self.body = body
self.category = category
self.image = image
self.id = id
self.createTime = createTime
}
Document class:
class Documents {
var id: String?
var title:String?
var createTime:CLong?
var url:String?
init (title:String, id:String, createTime:CLong?, url:String) {
self.title = title
self.id = id
self.createTime = createTime
self.url = url
}
ITSectionResult class:
class SectionSearchResult {
var title:String?
var image:String?
var id:String?
var byCategory:String?
init (title:String, image:String, id:String, byCategory:String) {
self.title = title
self.image = image
self.id = id
self.byCategory = byCategory
}
and finally the drafting BookmarkItem class:
class BookmarkItem:Object {
//Category
dynamic var bookmarkCategory:BookMarkItemCategory?
dynamic var title: NewsArticle?
dynamic var body:NewsArticle?
dynamic var category:NewsArticle?
dynamic var createTime:NewsArticle?
dynamic var image:NewsArticle?
dynamic var id:NewsArticle?
dynamic var link:String?
dynamic var url:String?
}
class BookMarkItemCategory:Object {
dynamic var name = ""
}
Here i would remind that, the BookmarkItem class uses 3 major properties to display in the BookmarkVC, "image" for filter category type(example: book image for Documents object, newspaper icon for News object) , "title" for the title and the url for display in WebView. Thank you so much, and wish you guys have a good weekend ahead.
As mentioned above in the comments by EpicPandaForce, this can be achieved by not having a class for each type and instead use a unified model.
import RealmSwift
final class UnifiedModel: Object {
dynamic var title: String = ""
dynamic var id: String = ""
dynamic var createTime: String = ""
dynamic var category: String = ""
dynamic var body: String?
dynamic var image: String?
override static func primaryKey() -> String {
return "id"
}
}
With the model I've shown above, every instance would need a title, id, create time and category while body and image could be left nil. Please feel free to follow-up if you have any other questions.
Disclaimer: I work for Realm.