Change array back to dictionary - SWIFT - arrays

I have a for loop that creates a dictionary and then I append the dictionary to an array. I append the dictionary to an array because I don't know how to add more than one value with the same key, when I do that in the for loop the key / value pair is just updated and the old key / value pair is deleted What is the best way to change the array back to a dictionary?
import UIKit
class ViewController: UIViewController {
var jobTitle = ""
var jobDescription = ""
var dict:[String: AnyObject] = ["jobTitle": "jobTitle", "jobDescription": "jobDescription"]
var tArray = [[String: AnyObject]]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
for var i = 0; i < 3; ++i {
jobTitle = "job1"
jobDescription = "Desc1"
dict["jobTitle"] = "job1"
dict["jobDescription"] = "Desc1"
tArray.append(dict)
}
println("\(tArray)")
}
}

Like such, where you have more than one value associated with each key:
let jobnames = ["j1", "j2", "j3"]
let jobdescs = ["d1", "d2", "d3"]
var dict : [String:[String]] = [:]
for index in 0..<3 {
if nil == dict["jobTitle"] { dict["jobTitle"] = [] }
if nil == dict["jobDesc" ] { dict["jobDesc" ] = [] }
dict["jobTitle"]!.append(jobnames[index])
dict["jobDesc" ]!.append(jobdescs[index])
}
Here is the output:

You call the same assignmenta such as
jobTitle = "job1"
at every iteration of the loop. Of course the variable will always contain the same value. The same is true for dict. It is an ivar, so you keep overwriting it.
What you want is to create a new collection of type [String: AnyObject] to add to your array.
let newDict:[String : AnyObject] = [titleKey : titleText,
descriptionKey : descriptionText]
tArray.append(newDict)

Related

How can I merge 2 dictionaries into one array?

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)
}

How to delete specific NSObject in Array with Swift3

I made table view with friends contact info.
And Each cell has button if touched,
I want to insert the info to selected friend array
(by the array, I made another small view to slide up with the friends list).
But If user the button one more,
I want to delete the friend Info in the selected friend array.
I know how to append to array,
but I don't know how to erase the specific item(NSObject) in array
by not using index.
my source code is below
class FriendModel : NSObject {
dynamic var index : ""
dynamic var thumbnail : ""
dynamic var name : ""
}
and In view controller class,
var selectedList = [FriendModel]()
#IBAction func SelectAct(_ sender: Any) {
let chooseBtn = sender as! UIButton
let indexPath = NSIndexPath(row: chooseBtn.tag, section:0)
let cell = tableView.cellForRow(at: indexPath as IndexPath) as! FriendsListSendCell
// when selected button is pushed
if chooseBtn.isSelected == true {
chooseBtn.isSelected = false
count = count! - 1
if self.count! < 1 {
self.windowShowUp = false
UIView.animate(withDuration: 0.1, delay: 0.1, options: [], animations:{self.selectedBoard.center.y += 80 }, completion: nil)
self.checkNumLabel.text = ""
}else{
}
//////////////////////////here////////////////////////////
//////////////////how to erase the FriendModel(NSObject) in selectedList.
}
//when the unselected button is pushed
else {
//instance for append the friend info
let friendInfo = FriendModel()
chooseBtn.isSelected = true
count = count! + 1
friendInfo.thumbnail = cell.thumbnail
friendInfo.name = cell.nameLabel.text!
//add friend info to selectedList
self.selectedList.append(friendInfo)
print("\(self.selectedList)")
if self.windowShowUp! == false{
self.windowShowUp = true
UIView.animate(withDuration: 0.1, delay: 0.1, options: [], animations:{self.selectedBoard.center.y -= 80 }, completion: nil)
}else{
}
}
}
You can use index(where:) to get index of your object and then remove item at index position.
class FriendModel {
var index = ""
var thumbnail = ""
var name = ""
}
let fm0 = FriendModel()
fm0.index = "100"
fm0.thumbnail = "tell.png"
fm0.name = "Phillips"
let fm1 = FriendModel()
fm1.index = "200"
fm1.thumbnail = "ask.png"
fm1.name = "Allen"
var array = [FriendModel]()
array.append(fm0)
array.append(fm1)
// The index below is an index of the array. Do not confuse with the FriendModel.index
let index = array.index {
return $0.thumbnail == "ask.png" && $0.name == "Allen"
}
array.forEach { print($0.name) }
print("Array index:", index ?? "n/a")
array.remove(at: index!)
array.forEach { print($0.name) }
You could use MirekE's solution which would work but here's an alternative solution.
First step would be identifying what the unique identifier on your FriendModel object is such as an id property.
Then on your model, since it is an NSObject, override the isEqual function like this:
override public func isEqual(object: AnyObject?) -> Bool {
let friend = object as? FriendModel
if self.id == friend?.id { return true }
return false
}
At this point you can use your desired form of iteration to find your element in the array and use remove.

How to append an array in another array in Swift?

I have a JSON response whose answer I have to parse. I write the single elements into an array called courseDataArray using a for loop. After that, I want to write this newly created array into another array called combinedCourseArray with the aim to pass that on to a UITableView. Creating the first array seems to work fine.
But how can I create another array combinedCourseArray who contain all arrays of type courseDataArray?
for (index, element) in result.enumerate() {
// get one entry from the result array
if let courseEntry = result[index] as? [String:AnyObject]{
//work with the content of the array
let courseName = courseEntry["name"]
let courseType = courseEntry["course_type"]
let courseDate = courseEntry["cor_date"]
let courseId = courseEntry["cor_id"]
let duration = courseEntry["duration"]
let schoolId = courseEntry["sco_id"]
let status = courseEntry["status"]
let courseDataArray = ["courseName" : courseName, "courseType": courseType, "courseDate": courseDate, "courseId": courseId, "duration": duration, "schoolId":schoolId, "status":status]
print(courseDataArray)
var combinedCourseArray: [String: AnyObject] = [:]
combinedCourseArray[0] = courseDataArray //does not work -- error: cannot subscript a value of type...
// self.shareData.courseStore.append(scooter)
}
You should move the combinedCourseArray declaration outside of the array. It should be var combinedCourseArray: [[String: AnyObject]] = [[:]] since it's an array and not a dictionary.
And you should be doing
combinedCourseArray.append(courseDataArray)
instead of
combinedCourseArray[0] = courseDataArray
var FirstArray = [String]()
var SecondArray = [String:AnyObject]()
FirstArray.append(contentsOf: SecondArray.value(forKey: "key") as! [String])
First declare this combinedCourseArray array out side this loop
var combinedCourseArray: [[String: AnyObject]] = [[String: AnyObject]]()
for (index, element) in result.enumerate() {
// get one entry from the result array
if let courseEntry = result[index] as? [String:AnyObject]{
//work with the content of the array
let courseName = courseEntry["name"]
let courseType = courseEntry["course_type"]
let courseDate = courseEntry["cor_date"]
let courseId = courseEntry["cor_id"]
let duration = courseEntry["duration"]
let schoolId = courseEntry["sco_id"]
let status = courseEntry["status"]
let courseDataArray = ["courseName" : courseName, "courseType": courseType, "courseDate": courseDate, "courseId": courseId, "duration": duration, "schoolId":schoolId, "status":status]
print(courseDataArray)
combinedCourseArray.append(courseDataArray) //does not work -- error: cannot subscript a value of type...
// self.shareData.courseStore.append(scooter)
}
}
Just use flatMap on the outer array to translate one array into another array, possibly dropping some elements:
let courseDataArray : [[String:AnyObject?]] = result.flatMap {
guard let courseEntry = $0 as? [String:AnyObject] else {
return nil
}
return [
"courseName" : courseEntry["name"],
"courseType": courseEntry["course_type"],
"courseDate": courseEntry["cor_date"],
"courseId": courseEntry["cor_id"],
"duration": courseEntry["duration"],
"schoolId": courseEntry["sco_id"],
"status": courseEntry["status"]
]
}
Of course, the guard isn't really necessary since the input type is presumably already [[String:AnyObject]] and since you then can't have any internal failures, you can just use map instead of flatMap

How to update swift dictionary value

I rewrite this code from php. And I find it difficult to make it work in swift.
var arrayOfData = [AnyObject]()
for index in 1...5 {
var dict = [String: AnyObject]()
dict["data"] = [1,2,3]
dict["count"] = 0
arrayOfData.append(dict)
}
for d in arrayOfData {
let data = d as AnyObject
// I want to update the "count" value
// data["count"] = 8
print(data);
break;
}
Presumably, you want to update the value inside of arrayOfData when you assign data["count"] = 8. If you switch to using NSMutableArray and NSMutableDictionary, then your code will work as you want. The reason this works is that these types are reference types (instead of value types like Swift arrays and dictionaries), so when you're working with them, you are referencing the values inside of them instead of making a copy.
var arrayOfData = NSMutableArray()
for index in 1...5 {
var dict = NSMutableDictionary()
dict["data"] = [1,2,3]
dict["count"] = 0
arrayOfData.addObject(dict)
}
for d in arrayOfData {
let data = d as! NSMutableDictionary
data["count"] = 8
print(data)
break
}
Assuming your array has to be of form '[AnyObject]' then something like this:
var arrayOfData = [AnyObject]()
for index in 1...5 {
var dict = [String: AnyObject]()
dict["data"] = [1,2,3]
dict["count"] = 0
arrayOfData.append(dict)
}
for d in arrayOfData {
// check d is a dictionary, else continue to the next
guard let data = d as? [String: AnyObject] else { continue }
data["count"] = 8
}
But preferably your array would be typed as an array of dictionaries:
var arrayOfData = [[String: AnyObject]]()
for index in 1...5 {
var dict = [String: AnyObject]()
dict["data"] = [1,2,3]
dict["count"] = 0
arrayOfData.append(dict)
}
for d in arrayOfData {
// swift knows that d is of type [String: AnyObject] already
d["count"] = 8
}
EDIT:
So the issue is that when you modify in the loop, you're creating a new version of the dictionary from the array and need to transfer it back. Try using a map:
arrayOfData = arrayOfData.map{ originalDict in
var newDict = originalDict
newDict["count"] = 8
return newDict
}
The most efficient way would be to find the index of the relevant values entry, and then replace that entry. The index is essentially just a pointer into the hash table, so it's better than looking up by key twice:
To update all the entries, you can loop through the indices one at a time:
for i in dictionary.values.indices {
dictionary.values[i].property = ...
}
To update a particular key, use:
let indexToUpdate = dictionary.values.index(forKey: "to_update")
dictionary.values[i].property = ...

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