How to filter a Model which having nested array in swift - swift

I am having model
class Consumer360PurchaseHistoryDetails: Codable {
var freqofPurchase : String?
var freqofVisit : String?
var customerPurchaseHistory : [CustomerPurchaseHistory]?
init() {
}
}
class CustomerPurchaseHistory : Codable {
var dateOfPurchase : String?
var products : [PurchaseProducts]?
init() {
}
}
class PurchaseProducts : Codable {
var productID : String?
var productFilterType : String?
init() {
}
}
I want to filter this model by productFilterType in PurchaseProducts
I tried the below way
var dataModel: Consumer360PurchaseHistoryDetails?
var tempDataModel:Consumer360PurchaseHistoryDetails = Consumer360PurchaseHistoryDetails()
for purchaseHistory in self.dataModel?.customerPurchaseHistory ?? [] {
for product in purchaseHistory.products ?? [] {
if product.productFilterType?.lowercased() == StringConstants.purchase {
tempDataModel.freqofVisit = "three"
tempDataModel.freqofPurchase = "five"
tempDataModel.customerPurchaseHistory?.append(purchaseHistory)
}
}
}
self.purchaseHistoryTableView.dataModel = self.tempDataModel
But the purchaseHistory is not getting appended in customerPurchaseHistory, which is is always nil after appending. But the freqofVisit and freqofPurchase is getting updated. Am i want use index for appending?

Your tempDataModel.customerPurchaseHistory? is set to nil by default. So the below code won't be executed.
tempDataModel.customerPurchaseHistory?.append(purchaseHistory)
Just above your for loop, assign its value to empty array, like this:
tempDataModel.customerPurchaseHistory = []
So, your code looks like this:
var dataModel: Consumer360PurchaseHistoryDetails?
var tempDataModel:Consumer360PurchaseHistoryDetails = Consumer360PurchaseHistoryDetails()
tempDataModel.customerPurchaseHistory = []
for purchaseHistory in self.dataModel?.customerPurchaseHistory ?? [] {
for product in purchaseHistory.products ?? [] {
if product.productFilterType?.lowercased() == StringConstants.purchase {
tempDataModel.freqofVisit = "three"
tempDataModel.freqofPurchase = "five"
tempDataModel.customerPurchaseHistory?.append(purchaseHistory)
}
}
}
self.purchaseHistoryTableView.dataModel = self.tempDataModel

Related

Can we use for loop for items of a struct?

I have a struct like this:
struct TestType {
var value1: String? = "1"
var value2: String? = "2"
var value3: String? = "3"
var value4: String? = "4"
var value5: String? = "5"
var value6: String? = "6"
var value7: String? = "7"
var value8: String? = "8"
var value9: String? = "9"
}
I want be able to use a for loop on values of TestType, like this code in below, is this possible in swift?
Or even any kind of loop support for items of a struct?
func myTestFunction() {
let test: TestType = TestType()
test(value1...value9).forEach { value in
if let unwrappedValue: String = value {
print(unwrappedValue)
}
}
}
You can use Mirror to achieve that, something like this:
struct TestType {
var value1: String? = "1"
var value2: String? = "2"
var value3: String? = "3"
var value4: String? = "4"
var value5: String? = "5"
var value6: String? = "6"
var value7: String? = "7"
var value8: String? = "8"
var value9: String? = "9"
func iterateThroughProperties() {
for property in Mirror(reflecting: self).children where property.label != nil {
print("name: \(property.label!)")
print("value: \(property.value)")
print("type: \(type(of: property.value))")
}
}
}
[String?](mirrorChildValuesOf: TestType())
public extension Array {
/// Create an `Array` if `subject's` values are all of one type.
/// - Note: Useful for converting tuples to `Array`s.
init?<Subject>(mirrorChildValuesOf subject: Subject) {
guard let array =
Mirror(reflecting: subject).children.map(\.value)
as? Self
else { return nil }
self = array
}
}

Nil data returned when copying "working" json data to new struc array

Weird. I swear this was working but then it just stopped working .. or ... Please ignore the i+i ,I will clean this up...
I don't have a clue why but myrecords?[i].title is returning nil. The json.releases[i].date_adde is working fine and full of data. I can "print" it and get a result. but when I go to copy it to the myrecords it is returning nil.
I download the data from JSON, that works fine. then I try to copy the data to a struc array I can get to in other parts of my app and now my myrecords data is empty. what the heck am I doing wrong?
import Foundation
var numberOfRecords : Int = 0
struct routine {
var dateadded : String
var title : String
var artist : String
var year : Int
var recordlabel : String
}
var myrecords: [routine]?
//-------------------------------------------------------------
struct Response: Codable {
var pagination: MyResult
var releases: [MyReleases]
}
public struct MyResult: Codable {
var page: Int
var per_page: Int
var items: Int
}
public struct MyReleases: Codable {
var date_added: String
var basic_information: BasicInformation
}
public struct BasicInformation: Codable {
var title: String
var year: Int
var artists : [Artist]
var labels: [Label]
}
public struct Artist: Codable {
var name: String
}
public struct Label: Codable {
var name: String
}
let url = "https://api.discogs.com/users/douglasbrown/collection/folders/0/releases?callback=&sort=added&sort_order=desc&per_page=1000"
public func getData(from url: String) {
let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { data, response, error in
guard let data = data, error == nil else {
print("something went wrong")
return
}
//HAVE DATA
var result: Response?
do {
result = try JSONDecoder().decode(Response.self, from: data)
}
catch {
print("Converion Error:\(error.localizedDescription)")
}
guard let json = result else {
return
}
numberOfRecords = json.pagination.items
var i: Int
i = -1
for _ in json.releases {
i = i + 1
myrecords?[i].dateadded = json.releases[i].date_added
myrecords?[i].title = json.releases[i].basic_information.title
myrecords?[i].artist = json.releases[i].basic_information.artists[0].name
myrecords?[i].year = json.releases[i].basic_information.year
myrecords?[i].recordlabel = json.releases[i].basic_information.labels[0].name
print(">>>>>>\(myrecords?[i].dateadded)")
}
})
task.resume()
}
You haven't initialized myrecords array.
Otherwise, you cannot use subscript like myrecords[i] when you don't know the capacity of array, it can be out of index.
First, initialize your array.
var myrecords: [routine]? = []
Second, append new element to array instead of using subscript
for _ in json.releases {
let newRecord = routine()
newRecord.dateadded = json.releases[i].date_added
newRecord.title = json.releases[i].basic_information.title
newRecord.artist = json.releases[i].basic_information.artists[0].name
newRecord.year = json.releases[i].basic_information.year
newRecord.recordlabel = json.releases[i].basic_information.labels[0].name
myrecords.append(newRecord)
}
This is the answer. :) THANK YOU All for pointing me in the right direction
struct Routine {
var dateadded : String
var title : String
var artist : String
var year : Int
var recordlabel : String
}
var myRecords: [Routine] = []
var i : Int
i = -1
for _ in json.releases {
var newRecord = Routine.self(dateadded: "", title: "", artist: "", year: 0, recordlabel: "")
i = i + 1
newRecord.dateadded = json.releases[i].date_added
newRecord.title = json.releases[i].basic_information.title
newRecord.artist = json.releases[i].basic_information.artists[0].name
newRecord.year = json.releases[i].basic_information.year
newRecord.recordlabel = json.releases[i].basic_information.labels[0].name
myRecords.append(newRecord)
}
print(">>>>\(myRecords[0].dateadded)")
I will clean up the bad code too but it works and that is good! :)

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.

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

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.