How can I merge 2 dictionaries into one array? - arrays

My JSON data look like this image below. Now I wanna merge the value of Shop Type and Promotion into one to use as collection view data. How can I do that?
I just filter the response data from the server like this:
var dataBanDau: [SDFilterModel] = []
var quickData: [SDFilterModel] = []
let filters: [SDFilterModel] = data
self.filterEntries = filters
//let nsarray = NSArray(array: self.filterEntries! , copyItems: true)
// self.filterEntriesStoreConstant = nsarray as! Array
self.dataBanDau = filters
for i in 0..<self.dataBanDau.count {
if self.dataBanDau[i].search_key.count == 0 {
self.quickData.append(self.dataBanDau[i])
}
}
self.quickData = self.quickData.filter {
$0.type != "range"
}
DispatchQueue.main.async {
//Note: Reload TableView
self.quickFilterCollection.reloadData()
completed(true)
}
}
the class SDFilterModel:
class SDFilterModel: DSBaseModel {
var name = String()
var type = String()
var is_expanded = Int()
var search_key = String()
var filterEntries : [SDFilterModel]?
override func copy(with zone: NSZone? = nil) -> Any {
// This is the reason why `init(_ model: GameModel)`
// must be required, because `GameModel` is not `final`.
let copy = SDFilterModel(dict: self.dictionary)
if let arrAttribute = NSArray(array: self.value , copyItems: true) as? [AttributeValueModel] {
copy.value = arrAttribute
}
return copy
}
override init(dict: Dictionary<String, Any>) {
super.init(dict: dict);
value = self.valueParse()
name = dict.getString(forKey: "name")
type = dict.getString(forKey: "type")
search_key = dict.getString(forKey: "search_key")
is_expanded = dict.getInt(forKey: "is_expanded")!
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
var value: [AttributeValueModel] = [];
func valueParse()-> [AttributeValueModel] {
guard let childs = (self.dictionary["value"]) as? [Dictionary<String, AnyObject>]
else { return [] }
var output: [AttributeValueModel] = [];
for aDict in childs {
let item = AttributeValueModel(dict:aDict);
// if type == .Range && item.option_id == "0" {
// item.setRangeOptionID(aValue: item.option_name!)
// }
//
output.append(item);
}
return output;
}

Let be Assume you have let myArray = [1,2,3,4,5,6,7,8]
Now you wanted to square of each and every element in the array,
With for loop you do like this
for item in myArray {
print(item * item)
}
Now assume item = $0
With for map you jus do
myArray.map({ $0 * $0 })
Both will gave same output.
map : Use to do same operation on every element of array.
flatmap : It is used to flattern the array of array.
let myArr = [[1,2,3],[4,5,6,7]]
and you want o/p as [1,2,3,4,5,6,7]
So can get above output with myArr.flatMap({$0})
Now back to your question.
let reqArray = myModel.data.map({ $0.value }).flatMap({ $0 })
First, map gaves you array-of-array of key value but you need a single array, so for that you need to use flatmap.
You can take ref : https://medium.com/#Dougly/higher-order-functions-in-swift-sorted-map-filter-reduce-dff60b5b6adf

Create the models like this
struct Option {
let name: String
let searchKey: String
let id: String
}
struct Model {
let type: String
let name: String
let isExpanded: Bool
let value: [Option]
}
You should get the options array values and join all the arrays
let models:[Model] = //...
let collectionViewArray = models.map { $0.value }.reduce([Option](), +)
Using for loop
var collectionViewArray = [Option]()
for model in models {
collectionViewArray.append(contentsOf: model.value)
}

Related

Iterate array of dictionary and sort based on key

I have array of dictionary and the values are as follows:
[["-MXpvzmZdbqzrjND8w9F": {
lid = "-MW6eEidZFCLeeZ0uBk1";
message = hi;
timeStamp = 1617960107264;
title = "Sambhar Dosa";
user = 1QSU0c1q8QNrZzmICXGClC0o86E3;
}, "-MXq5NAyrkk4ZcvRFM7T": {
lid = "-MW6eEidZFCLeeZ0uBk1";
message = "how ru?";
timeStamp = 1617962828647;
title = "Sambhar Dosa";
user = 1QSU0c1q8QNrZzmICXGClC0o86E3;
}], ["-MXqa5-pkC28lY_Q_hpZ": {
lid = "-MWwEpHAhIdhN0i5sltB";
message = "hi nice cycle";
timeStamp = 1617971142820;
title = "Cycle for kids";
user = 1QSU0c1q8QNrZzmICXGClC0o86E3;
}]]
Here there are 2 elements in the array. I want to take the last element in both of the array and sort it based on the timestamp value.
How to do it? Please help me.
From comments, it seems that that the type of the array is:
typealias SomeDataArray = [[String: [String: Any]]]
So we're missing type safety from the beginning. The first thing I'd do is define a struct to represent Any in that definition. For now I'll just use a struct as a wrapper for the inner dictionary, using computed properties for all the fields:
struct SomeData
{
let dict: [String: Any]
var lid: String? { dict["lid"] as? String }
var message: String? { dict["message"] as? String }
var timeStamp: Int { dict["timeStamp"] as? Int ?? 0 }
var title: String? { dict["title"] as? String }
// Skipping user, because I have no idea what to make of its type.
}
Really this should be decoded into some real Swift type, but that's a topic for another day.
So now we redo the typealias
typealias SomeDataArray = [[String: SomeData]]
In chat it was explained what within the "last" element for each of these dictionaries can be defined as the one with the largest timeStamp value. Given that this will give this solution (retaining the keys)
let results = testData.map {
dict in dict.map {
($0.key, SomeData(dict: $0.value))
}.sorted { $0.1.timeStamp < $1.1.timeStamp }.last
}.filter { $0 != nil }.map { $0! }.sorted { $0.1.timeStamp < $1.1.timeStamp }
If you want to transform it back the [[String;Any]] you get from Firebase, then it would be this:
let results = testData.map {
dict in dict.map {
($0.key, SomeData(dict: $0.value))
}.sorted { $0.1.timeStamp < $1.1.timeStamp }.last
}.filter { $0 != nil }.map { $0! }.sorted { $0.1.timeStamp < $1.1.timeStamp }
.map { [$0.0: $0.1.dict as Any] }

How to parse an array to return a collection?

I have a certain model in the form of a structure:
struct ContactsModel {
let name: String
let status: String
let number: String
let onlineStatus: Bool
}
And there is a method that parses the array of names and adds them to the new collection.
var contactsDictionary = [String: [String]]()
var contactNameSectionTitles = [String]()
var names = [String]()
var contactsArray = [ContactsModel(name: "Test", status: "Test", number: "+7 999 999 99 99", onlineStatus: true)]
func configurateDictionary() {
names = contactsArray.map {$0.name}
for value in names {
let nameKey = String(value.prefix(1))
if var namePrefix = contactsDictionary[nameKey] {
namePrefix.append(value)
contactsDictionary[nameKey] = namePrefix
} else {
contactsDictionary[nameKey] = [value]
}
}
contactNameSectionTitles = [String](contactsDictionary.keys)
contactNameSectionTitles = contactNameSectionTitles.sorted(by: { $0 < $1 })
}
How do I do so in the method so that it returns not a string, but a model? I want the collection to be type
var contactsDictionary = [String: [ContactsModel]]()
There is an API to group an array to a dictionary
var contactsDictionary = [String:[ContactsModel]]()
var contactNameSectionTitles = [String]()
func configurateDictionary() {
contactsDictionary = Dictionary(grouping: contactsArray, by: { $0.prefix(1).uppercased() })
contactNameSectionTitles = contactsDictionary.keys.sorted()
}
You need Dictionary grouping by , e.x to group the array by name do
let arr = [ContactsModel]
let res = Dictionary(grouping: arr, by: { $0.name }) // [String:[ContactsModel]]
https://developer.apple.com/documentation/swift/dictionary/3127163-init
This method will return a dictionary of [String: ContactsModel]
func configurateDictionary() -> [String: ContactsModel] {
let result = contactsArray.reduce( [String: ContactsModel](), { (d, e) -> [String: ContactsModel] in
var dict = d
dict[e.name] = e
return dict
})
return result
}
If you want to change the configurateDictionary method to return [String: [ContactsModel]], create a temporary dictionary inside the function and return it at the end of the function
class ViewController: UIViewController {
var contactsDictionary = [String: [ContactsModel]]()
var contactNameSectionTitles = [String]()
var names = [String]()
var contactsArray = [ContactsModel(name: "Test", status: "Test", number: "+7 999 999 99 99", onlineStatus: true)]
override func viewDidLoad() {
super.viewDidLoad()
contactsDictionary = configurateDictionary()
}
func configurateDictionary() -> [String: [ContactsModel]] {
names.removeAll()
contactNameSectionTitles.removeAll()
var temp = [String: [ContactsModel]]()
for contact in contactsArray {
names.append(contact.name)
let nameKey = String(contact.name.prefix(1))
temp[nameKey, default: []].append(contact)
}
contactNameSectionTitles = [String](contactsDictionary.keys).sorted()
return temp
}
}

Issue with storing data within an Array - Swift

I currently have my set-up as followed:
I am running a query in Firebase to extract all of the genres within an array of genres, like so:
var genresLabelIndex : [String] = ["Horror", "Fiction", "Romance"]
Then I am creating a blank arrays for each of the genres to be able to store the information of the genres within each of the areas like so:
var horrorData = [InformationForFeed]()
var fictionData = [InformationForFeed]()
var romanceData = [InformationForFeed]()
InformationForFeed looks like so:
class InformationForFeed {
fileprivate var _uploadKey:String!
fileprivate var _userKey:String!
fileprivate var _imageURL:String!
fileprivate var _socialMedia:[String]
var uploadKey:String!{
return _uploadKey
}
var userKey:String!{
return _userKey
}
var imageURL:String!{
return _imageURL
}
init(dictionary:Dictionary<String,AnyObject>, socials: [String]) {
_socialMedia = socials
if let uploadKey = dictionary["upload_key"] as? String {
self._uploadKey = uploadKey
}
if let userKey = dictionary["user_key"] as? String {
self._userKey = userKey
}
if let imageURL = dictionary["imageUrl"] as? String {
self._imageURL = imageURL
}
}
}
I am then creating an Array of the list of genres arrays like so:
1) First I am creating an empty array of arrays like this:
var genreArrayIndex : [[InformationForFeed]] = []
2) Then within my init() of the UIView I am setting what will be in the array like this:
genreArrayIndex = [self.horrorData, self.fictionData, self.romanceData]
I then will run a function called getData() that will run my query and start storing the information.
I store my information of each genre in a tempArray, and then I set the genreArrayIndex[index] to equal the tempArray and then clear the tempArray as seen in getData below.
func getData() {
for genre in genresLabelIndex {
let dbReference = Database.database().reference().child("genres").child(genre)
let query = dbReference.queryLimited(toLast: 6)
query.observeSingleEvent(of: .value, with: { (snapshot : DataSnapshot) in
if snapshot.childrenCount > 0 {
for s in snapshot.children.allObjects as! [DataSnapshot] {
let item = s.value as! Dictionary<String,AnyObject?>
let facebook = (s.childSnapshot(forPath: "social_links").value as? NSDictionary)?["facebook_link"]
let audible = (s.childSnapshot(forPath: "social_links").value as? NSDictionary)?["audible_link"]
let amazon = (s.childSnapshot(forPath: "social_links").value as? NSDictionary)?["amazon_link"]
var socialsArray = [String]()
if facebook != nil {
socialsArray.append(facebook! as! String)
} else {
socialsArray.append("nil")
}
if audible != nil {
socialsArray.append(audible! as! String)
} else {
socialsArray.append("nil")
}
if amazon != nil {
socialsArray.append(amazon! as! String)
} else {
socialsArray.append("nil")
}
let data = InformationForFeed(dictionary: item as Dictionary<String,AnyObject>, socials: socialsArray)
self.newArray.append(data)
}
}
self.genreArrayIndex[self.genreArrayIndexCount] = self.newArray
self.genreArrayIndexCount = self.genreArrayIndexCount + 1
self.newArray.removeAll()
self.internalIndex = self.internalIndex + 1
if self.internalIndex == self.genresLabelIndex.count {
self.tableView.reloadData()
}
})
}
}
My tempArray looks like this:
var newArray = [InformationForFeed]()
The index looks like this:
var genreArrayIndexCount : Int = 0
Now comes the issue....
All of the information is properly being stored in the genreArrayIndex .....but... it is not actually storing the information in the arrays that being stored in genreArrayIndex.
So in other words if you were to print(self.genreArrayIndex) it would be fully populated. But if you were to print(self.fictionData) it would be blank.
How can I resolve this?
Array is a value type. That means its contents are copies. Initializing genreArrayIndex with empty horrorData, (and others) and then filling it with getData() does not also copy the data back into horrorData. I would recommend eliminating
genreArrayIndex = [self.horrorData, self.fictionData, self.romanceData]
and replacing horrorData, fictionData, ... with computed properties. Perhaps like this.
var horrorData: [InformationFeed] {
return genreArrayIndex[0]
}

Appending Array Inside Function

I am trying to append values to an array inside a function and call that updated array in another function but it is not printing the new appended values.
I am using firebase and getting a snapshot of the database, sorting it, finding value of keys, and appending to hDates
var hDates:Array = [""]
getHistoryPGE() { hDates in
print(hDates)
self.hDates = [hDates]
}
func getHistoryPGE(completion: #escaping (String)->()) {
let userID = Auth.auth().currentUser?.uid
let ref = Database.database().reference().child("users").child(userID!)
ref.child("PostGameEval").observe(.value, with: { (snapshot) in
if let dict = snapshot.value as? [String : [String: [String: Any]]] {
let keys = Array(dict.keys)
var num : Int = 7
for i in 0..<keys.count {
if let innerDict = dict[keys[i]] {
let innerKeys = Array((innerDict).keys)
let sortedInnerKeys = innerKeys.sorted(by: { $0 > $1} )
while (sortedInnerKeys.count < num){
num -= 1
}
for j in 0..<num {
if let tempDict = innerDict[sortedInnerKeys[j]] {
print(tempDict["1abA"] as! String)
}
}
}
}
}})
}
func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int {
let dateString = self.dateFormatter.string(from: date)
if self.hDates.contains(dateString) {
return 1
}
return 0
}

Filtering arrays for use with UISearchBar

I have a table view which displays a user's Name, Company Name and Photo (PFFile). Each tableView row I have has all of this information in it.
I am using UISearchBarDelegate and IB to implement a search function to filter by the user's Name. It is finding the correct user but I have not been able to also update the company photo.
How do I filter the other arrays? The items I need from the arrays will be at the same index as the ones taken from the user's Name array.
EDIT: I am trying a different data structure and am receiving array index out of range, updated code below:
var filterArray = [User]() //<-- globally declared
var userArray = [User]() //< Global
class User {
var name: String?
var company: String?
init (name: String?, company: String?) {
self.name = name
self.company = company
}
}
//In a class which populates the search arrays
for object in unwrappedSucceeded {
let username = object.valueForKey("username") as! String
let companyName = object.valueForKey("companyName") as! String
let user = User(name: username, company: companyName)
userArray.append(user)
}
//tableViewController
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
filterArray.removeAll(keepCapacity: false)
if searchText.characters.count != 0 {
isSearch = true
self.search(searchText)
} else {
isSearch = false
}
}
func search(text: String) -> Void {
filterArray = userArray.filter({$0.name == text})
}
//In cellForRowAtIndexPath
cell.usernameCell.text = filterArray[indexPath.row].name //ARRAY INDEX OUT OF RANGE
Like I said you strongly recommend to group each user's info into one big container, therefore we could use array of struct or class, then it comes easier to filter.
schematic for the container:
struct Container
{
var username:String?
var companyName:String?
var photo:UIImage?
}
your main array will be : var arrayofData = [Container]()
Now when you are query your objects from parse, inside of your query function
// after you called the findObjectsWithBackgroundBlock()
// let's assume you check for error and if the [PFObject] is empty or not
for one in objectsFromParse
{
let photoToget = one["Photo"] as! PFFile
// next step should be to get the image data right :)
{
// let's assume that is the block when get the image data right:)
// check your data and assign it to some UIImage
// then
let userRepresentation = Container() //<-- we are creating a single object representation for each user
let username = one["username"] as! String //<--data we got from Parse
let companyName = one["companyName"] as! String
let userImage = //the UIImage which contains the data
userRepresentation.username = username
userRepresentation.companyName = companyName
userRepresentation.photo = userImage
// then we append
arrayOfData.append(userRepresentation)
}
}
Now we have all data into our array, so let's filter by username and also I hope you configure your tableView so when you have data from filter or regular array.
var filterArray = [Container]() //<-- globally declared
func search(text: String) -> Void
{
filterArray = arrayOfData.filter(){ (Container) -> Bool in
let range = Container.name!.rangeOfString(text, options:NSStringCompareOptions.CaseInsensitiveSearch) return range != nil }
// then you are good to go
}
let arr1 = [10,20,40]
let e1 = arr1.enumerate()
let arr2 = ["a","b","c"]
let f1 = e1.filter { $0.element % 20 == 0 }
let f2 = arr2.enumerate().filter { j, _ in
f1.contains { i, _ in
i == j
}
}
print(f1.map{$0.element}, f2.map{$0.element})
// [20, 40] ["b", "c"]
now you have both arrays "filtered". the best, what you can do is redesigning your data model!

Resources