String is not convertible from Dictionary<String,AnyObject> Error in Swift - swift

I've been battling this for too long. I have no idea
var a : [[String:AnyObject]] = [
[
"this":12
]
]
var b = "this"
func findAllKV(array: [[String:AnyObject]], key: String, value: AnyObject) -> [[String:AnyObject]] {
var all : [[String:AnyObject]] = []
for dict in array {
if dict[key] == value {
all.append(dict)
}
}
return all
}
findAllKV(a, b, 12)
I'm just trying to make a function that searches though an array of dictionaries and finds all with the matching key value

Try this one - println() helps reveal the issue:
var a : [[String:AnyObject]] = [
[
"this":12,
"test":13
],
[
"me":15,
"you":16
]
]
var b = "you"
func findAllKV(array: [[String:AnyObject]], key: String, value: AnyObject) -> [[String:AnyObject]] {
var all : [[String:AnyObject]] = []
for dict in array {
println(dict)
println(dict[key])
if let value: AnyObject = dict[key] {
println(value)
all += dict
}
}
return all
}
findAllKV(a, b, 12)

dict[key] returns an optional value. Try unwrapping it before checking:
var a : [[String:AnyObject]] = [
[
"this":12
]
]
var b = "this"
func findAllKV(array: [[String:AnyObject]], key: String, value: AnyObject) -> [[String:AnyObject]] {
var all : [[String:AnyObject]] = []
for dict in array {
if let val: AnyObject = dict[key] {
if val === value {
all.append(dict)
}
}
}
return all
}
var x = findAllKV(a, b, 12)
println(x) //[[this : 12 ]]

Related

Issue sorting dynamic sections in tableview | Swift

How can I sort the following sections made dynamically from the datestamp field? For example the sections called 2021-11-3 needs to be displayed in the tableview above 2021-11-2
datestamp format: 2021-11-3
var sections = [mySections]()
var structure = [myStructure]()
private func fetchJSON() {
guard let url = URL(string: "test.com")
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "id=\1".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
self.structure.sort { $0. datestamp > $1.datestamp }
let res = try decoder.decode([myStructure].self, from: data)
let grouped = Dictionary(grouping: res, by: { $0. datestamp })
let keys = grouped.keys.sorted()
self.sections = keys.map({mySections(date: $0, items: grouped[$0]!
)})
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
catch {
print(error)
}
}.resume()
}
I have tried doing the following under self.sections but it does not do anything:
self.sections.sorted { $0.date > $1.date }
Struct:
struct mySections {
let date : String
var items : [myStructure]
}
struct myStructure: Decodable {
let recordid: Int
let testname: Int
let datestamp: String
}
Example of Data:
[
{
"recordid": 1,
"testname": "Jen",
"datestamp": "2021-11-3"
},
{
"recordid": 1,
"testname": "Jake",
"datestamp": "2021-11-2"
}
]
Right now, you're sorting structure based on dateTimeStamp, which doesn't do anything, because structure doesn't have any data in it.
In my playground example, like your code, I used Dictionary(grouped:). Then, I map those results to mySections and sort by date.
The result of the print at the end is:
["2021-11-4", "2021-11-3", "2021-11-2"]
let data = """
[
{
"recordid": 1,
"testname": "Jake",
"datestamp": "2021-11-2"
},
{
"recordid": 2,
"testname": "Jen",
"datestamp": "2021-11-3"
},
{
"recordid": 3,
"testname": "Bob",
"datestamp": "2021-11-3"
},
{
"recordid": 4,
"testname": "Bill",
"datestamp": "2021-11-4"
}
]
""".data(using: .utf8)!
struct mySections {
let date : String
var items : [myStructure]
}
struct myStructure: Decodable {
let recordid: Int
let testname: String
let datestamp: String
}
var sections = [mySections]()
var structure = [myStructure]()
do {
let decoder = JSONDecoder()
let res = try decoder.decode([myStructure].self, from: data)
let grouped = Dictionary(grouping: res, by: { $0.datestamp })
sections = grouped.map { mySections(date: $0.key, items: $0.value) }
.sorted { $0.date > $1.date }
print(sections.map(\.date))
} catch {
print(error)
}
It's probably worth noting that right now your dates are stored in a uniform format that works well for string-based sorting. However, if your endpoint returned dates that weren't uniformly-formatted (like 2021-11-04 and 2021-11-3) you would want to convert to an actual Date first and sort based on that.

Firebase The data couldn’t be read because it isn’t in the correct format

I imported json manually (json file). I get this error when I decode the json.
The data couldn’t be read because it isn’t in the correct format.
where am I doing wrong? Is my model wrong? Is my reference wrong?
JSON:
{
"allQuiz": [
{
"title":"Ağustos Test 1",
"test": [
{
"id": 1,
"question":"Şekle göre aşağıdakiler hangisi doğrudur",
"isQuestionImage": true,
"isSectionImage": false,
"imageName":"1.png",
"sections": {
"A":"2 numaralı aracın öncelikle geçmesi",
"B":"1 numaralı aracın hızını arttırarak kavşağa girmesi",
"C":"2 numaralı aracın 3 numaralı aracın geçmesini beklemesi",
"D":"3 numaralı aracın 2 numaralı aracı ikaz ederek durdurması"
},
"correct": "A"
}
]
}
]
}
Model:
struct QuizContainer: Codable {
var allQuiz: [Quiz]
}
struct Quiz: Codable {
var title: String
var test: [Test]
}
enum TestSectionType: String, Codable, Hashable {
case A = "A"
case B = "B"
case C = "C"
case D = "D"
}
struct Test: Codable {
var id: Int
var question: String
var isQuestionImage: Bool
var isSectionImage: Bool
var imageName: String
var sections: [TestSectionType.RawValue : String]
var correct: String
}
JSON Decode:
func getQuizQuestion() {
let databaseReference = Database.database().reference().child("allQuiz")
databaseReference.observeSingleEvent(of: .value) { snapshot in
do {
let foo = try FirebaseDecoder().decode(QuizContainer.self, from: snapshot.value ?? "")
print(foo)
} catch {
print(error.localizedDescription)
}
}
}
Could the getQuizQuestion function be faulty?
I solved my problem.
I don't need reference.
func getQuizQuestion() {
let databaseReference = Database.database().reference() // here
databaseReference.observeSingleEvent(of: .value) { snapshot in
do {
let foo = try FirebaseDecoder().decode(QuizContainer.self, from: snapshot.value ?? "")
print(foo)
} catch {
print(error.localizedDescription)
}
}
}

Converting inherited class to JSON in Swift 4.2

I'm new to Swift, I'm trying to have this JSON
{
"title": "newSurvey",
"pages": [
{
"questions": [
{
"title": "Untitled Question",
"type": "QUESTION_SLIDER",
"content": {
"min": "1",
"max": "10",
"step": "3",
"defaultValue": "5"
}
},
{
"title": "asdasddfdf",
"type": "QUESTION_TEXT",
"choices": ["choice1","choice2"]
}
]
}
]}
I'm suffering from converting subclass to JSON
I thought about divide my code to three objects then add them to the final
string from jsonEncoder
so that's what I did
public class Question : Encodable {
var title : String?
var description: String?
init(_ title: String , _ desc: String) {
self.title = title
self.description = desc
}}
struct Questions : Encodable{
var questions : [Question]
}
class Create : Encodable {
var title : String?
var pages : [Questions] = []
init(_ title:String , _ q : Questions) {
self.title = title
self.pages.append(q)
}
func postData () -> String{
let jsonEncoder = JSONEncoder()
do {
jsonEncoder.outputFormatting = .prettyPrinted
let jsonData = try jsonEncoder.encode([self])
let json = String(data: jsonData ,encoding: .utf8)
print( json!)
return json!
}
catch {
print("Error")
}
return ""
}
class Content :Encodable {
init(_ min : Int , _ max : Int){
self.min = min
self.max = max
}
var min : Int?
var max : Int?
var minLabel : String?
var maxLabel : String?
var defaultValue : Int?
var step : Int?
}
class SliderQuestion :Question {
let TYPE = "SHORT TEXT"
var content = Content(0,2)
init(_ title: String, _ desc: String,_ content : Content ) {
self.content = content
super.init(title, desc)
}
}
I'm sorry for the long code but I want to clarify my idea, is there any way to have the subclass converted to JSON?

Matching separated arrays for Realm Object

I’m stuck with some problem. I have a JSON response from server:
{
"days" : [
{
"id" : 1,
"name" : "Day 1 - first day",
"url": "http://example.com/days/1"
},
{
"id" : 2,
"name" : "Day 2 - second day",
"url": "http://example.com/days/2"
},
...
],
"week" : [
{
"id" : 1,
"dayIds" : [1, 2, 6, 9, 23, 44, 2345],
"name" : "Rest week"
},
{
"id" : 35,
"dayIds" : [34,77,23,67,126,224],
"name" : "Educational week"
},
],
"plan" : {
"weekIds: [1, 6, 23, 74]
}
}
My data models (without mapping):
class Day: Object {
#objc dynamic var id: Int = -1
#objc dynamic var name: String = ""
#objc dynamic var url: String = ""
}
class Week: Object {
var dayIds = List<String>()
#objc dynamic var name: String = ""
#objc dynamic var id: Int = -1
var days: List<Week>? = nil
}
class Plan: Object {
var weekDays = List<String>()
var weeks: List<Week>? = nil
}
Mapping code:
let json: [String: Any] = try! JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
let plans: [Plan] = Mapper<Plan>().mapArray(JSONArray: json["plans"] as! [[String: Any]])
let days: [Day] = Mapper<Day>().mapArray(JSONArray: json["days"] as! [[String: Any]])
let weeks: [Week] = Mapper<Week>().mapArray(JSONArray: json["weeks"] as! [[String: Any]])
So, I need to tell realm that an array weeks belong to plan.weeks and an array days belong to object week.days and related by theirs id’s. How can I do this more simply? Do you have any ideas?
The alternative solution is in-head brute force like this.
for week in weeks {
for dayId in week.dayIds {
for day in days {
if day.id == dayId {
week.days.append(day)
}
}
}
}
for plan in plans {
for week in weeks {
for weekId in plans.weekIds {
if weekId == week.id {
plan.weeks.append(week)
}
}
}
}
I believe that somewhere exist more pure and simple solution :)
Thanks.
Your data structures seem very nested, so you're going to have to do the internal looping. If you want something more swifty, use map and filter here instead of for loops:
let days = weeks.map({
$0.dayIds.map({
$0.filter({
$0.id == dayId
})
})
})

Filter array of objects with multiple criteria and types in Swift

I am trying to do some complex filtering in my app and I am to a point where I don't know what to do next. My data consistes of an array of dictionaries where the values in each of the dictionaries can be String, Int or [String].
let person1: [String : Any] = ["first_name" : "John",
"last_name" : "Smith",
"age" : 21,
"skills" : ["C#", "Java", "Swift"]]
let person2: [String : Any] = ["first_name" : "Kim",
"last_name" : "Smith",
"age" : 28,
"skills" : ["Java", "Swift"]]
let person3: [String : Any] = ["first_name" : "Kate",
"last_name" : "Bell",
"age" : 24,
"skills" : ["C#"]]
var people = [person1, person2, person3]
I let the user choose how to filter this data and create a dictionary of filter criteria. This dictionary can have any number of keys and values.
let filters: [String : [Any]] = ["age" : [28, 24],
"skills" : ["Java", "Swift"]]
In this example I want to show persons who are age 28 or 24 and have a skills of Java or Swift, which would be person2
Here is what I have so far but it only works with Int values:
for (key, values) in filters {
var filteredItems = people.filter {
var match = false
for filterValue in values {
if $0[key] as! Int == filterValue as! Int {
match = true
break
}
else {
match = false
}
}
return match
}
people = filteredItems
}
Here's how I would do this:
struct Person {
let firstName: String
let lastName: String
let age: Int
let skills: [String]
enum Filter {
enum FilterType<T: Hashable> {
case one(of: [T])
case all(of: [T])
// Match against a property that's a single value
func matches(_ value: T) -> Bool {
switch self {
case .one(let filterValues): return filterValues.contains(value)
case .all(let filterValues): return filterValues.count == 1 && filterValues[0] == value
}
}
// Match against a property that's a list of values
func matches(_ values: [T]) -> Bool {
switch self {
case .one(let filterValues): return !Set(filterValues).intersection(values).isEmpty
case .all(let filterValues): return Set(filterValues).isSuperset(of: values)
}
}
}
case age(is: FilterType<Int>)
case skills(is: FilterType<String>)
func matches(_ p: Person) -> Bool {
switch self {
case .age(let filterValues): return filterValues.matches(p.age)
case .skills(let filterValues): return filterValues.matches(p.skills)
}
}
}
}
extension Array where Element == Person.Filter {
func atLeastOneMatch(_ p: Person) -> Bool {
self.contains(where: { $0.matches(p) })
}
func matchesAll(_ p: Person) -> Bool {
self.allSatisfy { $0.matches(p) }
}
}
let people = [
Person(
firstName: "John",
lastName : "Smith",
age: 21,
skills: ["C#", "Java", "Swift"]
),
Person(
firstName: "Kim",
lastName : "Smith",
age: 28,
skills: ["Java", "Swift"]
),
Person(
firstName: "Kate",
lastName: "Bell",
age: 24,
skills: ["C#"]
),
]
let filters: [Person.Filter] = [
.age(is: .one(of: [28, 24])),
.skills(is: .one(of: ["Java", "Swift"])),
]
let peopleWhoMatchAllFilters = people.filter(filters.matchesAll)
print(peopleWhoMatchAllFilters)
let peopleWhoMatchAtLeastOneFilter = people.filter(filters.atLeastOneMatch)
print(peopleWhoMatchAtLeastOneFilter)
I've extended the filtering capability to be able to specify wether all values of a filter should be matched (e.g. a person must know Java AND Swift AND C#) or at least one (e.g. a person must know AT LEAST Java OR Swift OR C#)