Convert an array of string to an array of images - arrays

I have an array of string which to convert to an array of images.
var inviteStatus = [Any]()
inviteStatus = ["accepted", "accepted", "pending", "pending"]
When the invite status is "accepted", I want it to be replaced with an image at that index. The desired result is this:
inviteStatus = [UIImage(named: "accepted.png"), UIImage(named: "accepted.png"), UIImage(named: "pending.png"),, UIImage(named: "pending.png")]
I tried with this following code but it's not working:
for (index, str) in self.arrayInviteStatus.enumerated() {
self.arrayInviteStatus[index] = str.replacingOccurrences(of: "accepted", with: UIImage(name: "accepted.png"))
self.arrayInviteStatus[index] = str.replacingOccurrences(of: "pending", with: UIImage(name: "pending"))
}
Thank you for your help.

Use map
let inviteStatusImages = inviteStatus.map{ UIImage(named: $0+".png") }
You are discouraged from using [Any]. Better use two separate arrays with distinct types.

I think map is definitely the way to go, but maybe consider using an enum, so you get a type safe way to distinguish between the two cases:
enum InviteStatus {
case accepted
case pending
}
Now you could have an Array of InviteStatus and combine map with pattern matching:
let inviteStatus: [InviteStatus] = [.accepted, .accepted, .pending, .pending]
func statusToImage(status: InviteStatus) -> UIImage {
switch status {
case let .accepted: return UIImage(named: "accepted.png")
case let .pending: return UIImage(named: "pending.png")
}
}
let imageArray = inviteStatus.map(statusToImage)
What this gives you is the ability to easily refactor without losing type safety. E.g. if you want to add another status case in the future like "declined", or something similar, you can add it to your enum and the switch-case pattern match will tell you at compile time that you'll have to add another case to it.

you can do like this
var inviteStatus: [String] = []
inviteStatus = ["accepted", "accepted", "pending", "pending"]
var imgArray: [UIImage] = []
imageArray = inviteStatus.map { UIImage(named: "\($0).png") }

You can try to create an integrate array and then pass the variables to the original array
var inviteStatus = [Any]()
inviteStatus = ["accepted", "accepted", "pending", "pending"]
//inviteStatus = [UIImage(named: "accepted.png"), UIImage(named: "accepted.png"), UIImage(named: "pending.png"),, UIImage(named: "pending.png")]
var newArray = [Any]()
for invStat in inviteStatus {
newArray.append(UIImage(named: "\(invStat).png"))
}
inviteStatus.removeAll()
for na in newArray{
inviteStatus.append(na)
}

Related

Swift and JSON questions

I'm kind of new to Swift currently playing around with Stickers.
I have a JSON file with the following structure:
{
"stickers": [{
"filename": "happy_face",
"description": "Happy Face",
"premium": "false",
"categories": ["blue", "green", "red"],
"genders": ["male", "female"]
},{
"filename": "sad_face",
"description": "Sad Face",
"premium": "false",
"categories": ["blue", "green", "red", "yellow"],
"genders": ["male"]
}]
}
Stickers will have the same filename, but will be separated into folders according to their category and gender.
I can read the JSON data just fine. My problem is when I'm trying to make some use of the JSON data.
My goal is to separate the stickers according to their categories, which could vary depending on the sticker, the user will later be able to switch categories, and the correct stickers will be displayed.
let stickerPack = StickerPack.load()
let allStickers = stickerPack!["stickers"] as? [[AnyHashable : Any]]
func getStickersWithCategory(category: String){
var stickers = [AnyObject]()
for sticker in allStickers! {
let cat = sticker["categories"] as? [String]
for item in cat! {
if item.contains(category){
stickers.append(sticker)
}
}
}
}
The result of this is
"Argument type '[AnyHashable : Any]' does not conform to expected type 'AnyObject'"
Can anyone point me in the right direction? Is it possible with this JSON structure? or is it better to have a different structure, with each category and gender separated? this would lead to a lot of repetition. But maybe I'm creating more problems by trying to keep the JSON structure this way.
All help appreciated!
Be more type specific, this avoids that kind of errors.
This JSON array is [[String:Any]] and never AnyHashable because the keys are required to be string
let allStickers = stickerPack!["stickers"] as? [[String:Any]]
stickers is the same type and never something including AnyObject. All JSON types are value types (Any).
var stickers = [[String:Any]]()
You can filter the array swiftier and it's recommended to safely unwrap all optionals
if let stickerPack = StickerPack.load(),
let allStickers = stickerPack["stickers"] as? [[String:Any]] {
stickers = allStickers.filter({ (sticker) -> Bool in
guard let categories = sticker["categories"] as? [String] else { return false }
return categories.contains(category)
})
...
The issue is that you declared allStickers as let allStickers = stickerPack!["stickers"] as? [[AnyHashable : Any]], which means that its type will be [[AnyHashable:Any]]? or Array<Dictionary<AnyHashable:Any>>. When you are iterating through that array in for sticker in allStickers!, the type of sticker will be Dictionary<AnyHashable:Any>. Dictionary is a struct, so it doesn't conform to AnyObject, and hence you cannot add sticker to stickers, which is declared as an array of AnyObjects.
So changing var stickers = [AnyObject]() to var stickers = [Any](), or more specifically var stickers = [[AnyHashable:Any]]() should solve your issue.
func getStickersWithCategory(category: String){
var stickers = [[AnyHashable:Any]]()
for sticker in allStickers! {
let cat = sticker["categories"] as? [String]
for item in cat! {
if item.contains(category){
stickers.append(sticker)
}
}
}
}
Btw you are encouraged to use Codable in Swift 4 when handling JSON.

How to get an element of an array inside another array?

So the only way i can think of achieving this is by putting the array inside mainArray into a variable and then indexing that. Is there an easier way?
mainArray = [ 3400, "Overwatch", [UIButton(), UIButton()]] // Some buttons already made
currentButtonArray = mainArray[mainArray.count - 1] as! NSArray
for i in 0..<currentButtonArray.count {
buttonArray.append( currentButtonArray[i] as! UIButton)
}
If there is one array containing only UIButton instances, just filter it.
let mainArray : [Any] = [3400, "Overwatch", [UIButton(), UIButton()]]
if let buttonArray = mainArray.filter({$0 is [UIButton]}).first as? [UIButton] {
print(buttonArray)
}
or
let buttonArray = Array(mainArray.flatMap{$0 as? [UIButton]}.joined())
The second approach returns a non-optional empty array if there is no array of UIButton in mainArray
If the subarray is of all one type, you can append all in one go:
var buttonArray = [UIButton]()
let mainArray:[Any] = [3400, "Overwatch", [UIButton(), UIButton()]] // Some buttons already made
if let currentButtonArray = mainArray.last as? [UIButton] {
buttonArray.append(contentsOf: currentButtonArray)
}
Or you could simply write:
guard let currentButtonArray = mainArray.last as? [UIButton] else {
// do something e.g. return or break
fatalError()
}
// do stuff with array e.g. currentButtonArray.count
If you didn't know the position in the array of the nested UIButton array or if there were multiple nested button arrays then this would work:
let buttonArray = mainArray.reduce([UIButton]()){ (array, element) in if let bArray = element as? [UIButton] {
return array + bArray
}
else {
return array
}
}
Note: this is Swift 3 code.

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 we can find an element from [AnyObject] type array in swift

I have [AnyObject] array
var updatedPos = [AnyObject]()
I am setting data in that according to my requirement like!
let para:NSMutableDictionary = NSMutableDictionary()
para.setValue(posId, forKey: "id")
para.setValue(posName, forKey: "job")
let jsonData = try! NSJSONSerialization.dataWithJSONObject(para, options: NSJSONWritingOptions())
let jsonString = NSString(data: jsonData, encoding: NSUTF8StringEncoding) as! String
self.updatedPos.append(jsonString)
Now in my code i have some requirement to remove the object from this array where id getting matched according to requirement Here is the code which i am trying to implement
for var i = 0; i < updatedPos.count; i++
{
let posItem = updatedPos[i]
print("Id=\(posItem)")
let pId = posItem["id"] as? String
print("secRId=\(pId)")
if removeId! == pId!
{
updatedPos.removeAtIndex(i)
}
}
Here print("Id=\(posItem)") give me output asId={"id":"51","job":"Programmer"} but here i am not able to access id from this object. here print("secRId=\(pId)") give me nil
First of all use native Swift collection types.
Second of all use types as specific as possible.
For example your [AnyObject] array can be also declared as an array of dictionaries [[String:AnyObject]]
var updatedPos = [[String:AnyObject]]()
Now create the dictionaries and add them to the array (in your example the dictionary is actually [String:String] but I keep the AnyObject values).
let para1 : [String:AnyObject] = ["id" : "51", "job" : "Programmer"]
let para2 : [String:AnyObject] = ["id" : "12", "job" : "Designer"]
updatedPos.append(para1)
updatedPos.append(para2)
If you want to remove an item by id use the filter function
let removeId = "12"
updatedPos = updatedPos.filter { $0["id"] as? String != removeId }
or alternatively
if let indexToDelete = updatedPos.indexOf{ $0["id"] as? String == removeId} {
updatedPos.removeAtIndex(indexToDelete)
}
The JSON serialization is not needed for the code you provided.
PS: Never write valueForKey: and setValue:forKey: unless you know exactly what it's doing.
After some little bit stuffs I have found the very easy and best solution for my question. And I want to do special thanks to #vadian. Because he teach me new thing here. Hey Thank you very much #vadian
Finally the answer is I had covert posItem in json Format for finding the id from Id={"id":"51","job":"Programmer"} this string
And the way is
let data = posItem.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
if let dict = json as? [String: AnyObject] {
let id = dict["id"]
if removeId! == id! as! String
{
updatedLoc.removeAtIndex(i)
}
}
}
catch {
print(error)
}

Swift 2 - Create a array of arrays

Im trying to make an array with multiple arrays inside of it.
Im using CloudKit to get the data.
import UIKit
import CloudKit
var questionsCount = 0
var questionsArray = [String]()
class hvadvilduhelstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//firstfield.setTitle("Klik her for at starte!", forState: .Normal)
//secondfield.setTitle("Klik her for at starte!", forState: .Normal)
firstfield.titleLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping
firstfield.titleLabel?.textAlignment = NSTextAlignment.Center
secondfield.titleLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping
secondfield.titleLabel?.textAlignment = NSTextAlignment.Center
let container = CKContainer.defaultContainer()
let publicData = container.publicCloudDatabase
let query = CKQuery(recordType: "Questions", predicate: NSPredicate(format: "TRUEPREDICATE", argumentArray: nil))
publicData.performQuery(query, inZoneWithID: nil) { results, error in
if error == nil { // There is no error
for entry in results! {
let firstOne = [entry["Question1"] as! String]
let secondOne = firstOne + [entry["Question2"] as! String]
let thirdOne = secondOne + [String(entry["Question1Rating"] as! Int)]
let fourthOne = thirdOne + [String(entry["Question2Rating"] as! Int)]
let fithOne = fourthOne + [String(entry["Reports"] as! Int)]
questionsArray = questionsArray + fithOne
print(questionsArray)
}
}
else {
print(error)
}
}
}
Using previous code I am getting this in the console output:
["Dette er en test1", "Dette er en test2", "0", "0", "0", "test2", "test2", "0", "0", "0"]
instead of this (which is the output i want):
[["Dette er en test1", "Dette er en test2", "0", "0", "0"], ["test2", "test2", "0", "0", "0"]]
I simple can't figure out how to do this. My plan was to get a lot of records and put them inside of this single, huge array (to make it easy to use the 'value') Is there an easier/better way to do this?
Sorry for my english, not my native language.
Thanks for your help!
When you have a problem like this, make a Playground and experiment and do a little thinking. Here is what you are doing, in essence:
var arr = [String]()
for _ in (1...3) {
let first = ["Mannie"]
let second = first + ["Moe"]
let third = second + ["Jack"]
arr = arr + third
}
arr // ["Mannie", "Moe", "Jack", "Mannie", "Moe", "Jack", "Mannie", "Moe", "Jack"]
That isn't what you want, so don't do that. First, as your question title says, you want an array of arrays. Well then, you don't want to end up with a [String]; you just told us that you want a [[String]] (an array of arrays of strings)! So first make that change:
var arr = [[String]]()
Now, when you build your array and insert it into your array of arrays, use the append method (instead of the + operator):
arr.append(third)
Here's the result:
var arr = [[String]]()
for _ in (1...3) {
let first = ["Mannie"]
let second = first + ["Moe"]
let third = second + ["Jack"]
arr.append(third)
}
arr // [["Mannie", "Moe", "Jack"], ["Mannie", "Moe", "Jack"], ["Mannie", "Moe", "Jack"]]
Now go ye and do likewise in your real code.
Try this instead:
import UIKit
import CloudKit
var questionsCount = 0
var questionsArray = [[String]]()
class hvadvilduhelstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//firstfield.setTitle("Klik her for at starte!", forState: .Normal)
//secondfield.setTitle("Klik her for at starte!", forState: .Normal)
firstfield.titleLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping
firstfield.titleLabel?.textAlignment = NSTextAlignment.Center
secondfield.titleLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping
secondfield.titleLabel?.textAlignment = NSTextAlignment.Center
let container = CKContainer.defaultContainer()
let publicData = container.publicCloudDatabase
let query = CKQuery(recordType: "Questions", predicate: NSPredicate(format: "TRUEPREDICATE", argumentArray: nil))
publicData.performQuery(query, inZoneWithID: nil) { results, error in
if error == nil { // There is no error
for entry in results! {
let firstOne = entry["Question1"] as! String
let secondOne = entry["Question2"] as! String
let thirdOne = String(entry["Question1Rating"] as! Int)
let fourthOne = String(entry["Question2Rating"] as! Int)
let fifthOne = String(entry["Reports"] as! Int)
questionsArray.append([firstOne, secondOne, thirdOne, fourthOne, fifthOne])
print(questionsArray)
}
}
else {
print(error)
}
}
}
}
Notably, questionsArray is now of type [[String]], not just [String], and for each entry, you want to append a new [String]

Resources