I would like to create a Swift TableView for all available sizes of coffee within a specific type. With this JSON tree, how can you structure the class to create an array for the type specific sizes?
The JSON Tree is structured as follows:
{
"_id": "60ba1ab72e35f2d9c786c610",
"types": [
{
"_id": "60ba1a062e35f2d9c786c56d",
"name": "Ristretto",
"sizes": [
"60ba18d13ca8c43196b5f606",
"60ba3368c45ecee5d77a016b"
],
"extras": [
"60ba197c2e35f2d9c786c525"
]
},
{
"_id": "60be1db3c45ecee5d77ad890",
"name": "Espresso",
"sizes": [
"60ba3368c45ecee5d77a016b",
"60ba33dbc45ecee5d77a01f8"
],
"extras": [
"60ba34a0c45ecee5d77a0263"
]
},
{
"_id": "60be1eabc45ecee5d77ad960",
"name": "Cappuccino",
"sizes": [
"60ba18d13ca8c43196b5f606",
"60ba3368c45ecee5d77a016b",
"60ba33dbc45ecee5d77a01f8"
],
"extras": [
"60ba197c2e35f2d9c786c525",
"60ba34a0c45ecee5d77a0263"
]
}
],
"sizes": [
{
"_id": "60ba18d13ca8c43196b5f606",
"name": "Large",
"__v": 0
},
{
"_id": "60ba3368c45ecee5d77a016b",
"name": "Venti"
},
{
"_id": "60ba33dbc45ecee5d77a01f8",
"name": "Tall"
}
],
"extras": [
{
"_id": "60ba197c2e35f2d9c786c525",
"name": "Select the amount of sugar",
"subselections": [
{
"_id": "60ba194dfdd5e192e14eaa75",
"name": "A lot"
},
{
"_id": "60ba195407e1dc8a4e33b5e5",
"name": "Normal"
}
]
},
{
"_id": "60ba34a0c45ecee5d77a0263",
"name": "Select type of milk",
"subselections": [
{
"_id": "611a1adeff35e4db9df19667",
"name": "Soy"
},
{
"_id": "60ba348d8c75424ac5ed259e",
"name": "Oat"
},
{
"_id": "60ba349a869d7a04642b41f4",
"name": "Cow"
}
]
}
]
}
My classes: (I can currently load a TableView with the coffee sizes, but not the type specific sizes)
import Foundation
import UIKit
class Types: Codable {
let types: [CoffeeType]
init(types: [CoffeeType]) {
self.types = types
}
}
class Sizes: Codable {
let sizes: [CoffeeType]
init(sizes: [CoffeeType]) {
self.sizes = sizes
}
}
class CoffeeType: Codable {
let _id: String
let name: String
init(_id: String, name: String) {
self._id = _id
self.name = name
}
}
The app is structured like this: HomeViewController is a TableView of Types. When you click on a Type, you transition to SelectSizeViewController, to which I have already assigned a Type through the segue. On SelectSizeViewController, I would like to display the list of specific sizes.
use QuickType to get the model this is quite helpful. you will be getting the model easily(suggestion). I think you are using the wrong model.
if your Base model struct is correct it should be something like this
struct Coffie: Codable {
let id: String
let types: [TypeElement]
let sizes: [Size]
let extras: [Extra]
enum CodingKeys: String, CodingKey {
case id
case types, sizes, extras
}
}
from here itself you can get the type and sizes with corresponded id's you can filter size values from sizes array
Related
I have a products catalogue where every product is indexed as follows (queried from http://localhost:9200/products/_doc/1) as sample:
{
"_index": "products_20201202145032789",
"_type": "_doc",
"_id": "1",
"_version": 1,
"_seq_no": 0,
"_primary_term": 1,
"found": true,
"_source": {
"title": "Roncato Eglo",
"description": "Amazing LED light made of wood and description continues.",
"price": 3990,
"manufacturer": "Eglo",
"category": [
"Lights",
"Indoor lights"
],
"options": [
{
"title": "Mount type",
"value": "E27"
},
{
"title": "Number of bulps",
"value": "4"
},
{
"title": "Batteries included",
"value": "true"
},
{
"title": "Ligt temperature",
"value": "warm"
},
{
"title": "Material",
"value": "wood"
},
{
"title": "Voltage",
"value": "230"
}
]
}
}
Every option contains different value, so there are many Mount type values, Light temperature values, Material values, and so on.
How can I create an aggregation (filter) where I can let customers choose between various Mount Type options:
[ ] E27
[X] E14
[X] GU10
...
Or let them choose from different Material options displayed as checkboxes:
[X] Wood
[ ] Metal
[ ] Glass
...
I can handle it on frontend once the buckets are created. Creation of different buckets for these options is What I am struggling with.
I have succesfully created and displayed and using aggregations for Category, Manufacturer and other basic ones. Thes product options are stored in has_many_through relationships in database. I am using Rails + searchkick gem, but those allow me to create raw queries to elastic search.
The prerequisite for such aggregation is to have options field as nested.
Sample index mapping:
PUT test
{
"mappings": {
"properties": {
"title": {
"type": "keyword"
},
"options": {
"type": "nested",
"properties": {
"title": {
"type": "keyword"
},
"value": {
"type": "keyword"
}
}
}
}
}
}
Sample docs:
PUT test/_doc/1
{
"title": "Roncato Eglo",
"options": [
{
"title": "Mount type",
"value": "E27"
},
{
"title": "Material",
"value": "wood"
}
]
}
PUT test/_doc/2
{
"title": "Eglo",
"options": [
{
"title": "Mount type",
"value": "E27"
},
{
"title": "Material",
"value": "metal"
}
]
}
Assumption: For a given document a title under option appears only once. For e.g. there can exists only one nested document under option having title as Material.
Query for aggregation:
GET test/_search
{
"size": 0,
"aggs": {
"OPTION": {
"nested": {
"path": "options"
},
"aggs": {
"TITLE": {
"terms": {
"field": "options.title",
"size": 10
},
"aggs": {
"VALUES": {
"terms": {
"field": "options.value",
"size": 10
}
}
}
}
}
}
}
}
Response:
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"OPTION" : {
"doc_count" : 4,
"TITLE" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "Material",
"doc_count" : 2,
"VALUES" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "metal",
"doc_count" : 1
},
{
"key" : "wood",
"doc_count" : 1
}
]
}
},
{
"key" : "Mount type",
"doc_count" : 2,
"VALUES" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "E27",
"doc_count" : 2
}
]
}
}
]
}
}
}
}
The problem
I am currently experiencing issues with decoding generic keys in a JSON. My current implementation accepts 3 keys primary, secondary, tertiary. However in the future I want to have the key of JSON dictionaries to be generic. I have tried to implement a similar way as stated in this tutorial: https://benscheirman.com/2017/06/swift-json/. Unfortunately I can not get it working and some help is really welcome.
My question ain't no duplicate of the below one
The following post handles a way different level of generic"nes": How to deal with completely dynamic JSON responses therefore my question is a lot more concise than the one that market this question as duplicate with the post above..
current JSON
{
"primary": {
"color": [3,111,66,1],
"font": {
"name": "UniversLTStd-UltraCn",
"size": "16"
}
},
"secondary": {
"color": [11,34,56,1],
"font": {
"name": "UniversLTStd-UltraCn",
"size": "16"
}
},
"tertiary": {
"color": [233,222,211,1],
"font": {
"name": "UniversLTStd-UltraCn",
"size": "16"
}
}
}
wished / possible JSON
{
"SomeKey": {
"color": [3,111,66,1],
"font": {
"name": "UniversLTStd-UltraCn",
"size": "16"
}
},
"OtherKey": {
"color": [11,34,56,1],
"font": {
"name": "UniversLTStd-UltraCn",
"size": "16"
}
},
"AnotherKey": {
"color": [233,222,211,1],
"font": {
"name": "UniversLTStd-UltraCn",
"size": "16"
}
}
}
The decodable structs can be found here: https://pastebin.com/ZYafkDNH
The question
How can I migrate my current code to accepts dynamic keys (at the place of primary, secondary, tertiary..) so I do not have to hard code them in the Base/Root Struct which can be found in Theme now.
You can try to parse it as a dictionary of [String:Key] instead of hardcoding the keys , by that it'll be parsed if keys are changed , but you have to do some logic inside the app to know which value corresponds to a specified key
let res = try? JSONDecoder().decode([String:Key].self, from: jsonData)
struct Key: Codable {
let color: [Int]
let font: Font
}
struct Font: Codable {
let name, size: String
}
As you seem to be responsible for the JSON I recommend to change the structure to an array and a type property.
[{"type": "primary",
"appearance": {
"color": [3,111,66,1],
"font": {
"name": "UniversLTStd-UltraCn",
"size": "16"
}
}
},
{
"type": "secondary",
"appearance": {
"color": [11,34,56,1],
"font": {
"name": "UniversLTStd-UltraCn",
"size": "16"
}
}
},
{
"type": "tertiary",
"appearance": {
"color": [233,222,211,1],
"font": {
"name": "UniversLTStd-UltraCn",
"size": "16"
}
}
}]
which is much easier to maintain.
The corresponding structs are
struct Theme : Decodable {
let type : String // could be even a custom enum
let appearance : Appearance
}
struct Appearance: Decodable {
let color: [UInt8]
let font: Font
}
struct Font: Decodable {
let name, size: String
}
and decode the JSON to [Theme].self
Otherwise as suggested by Sh_Khan you have to decode a dictionary or you have to write a custom initializer.
I have to parse this json Dictionary having different keys.
In this example only object_summary key is same while the other keys are different in all the objects.
I want to parse this using Swift 4 Decodable protocol with JSONDecoder(). Please help.
{
"car": {
"object_summary": {
"type": "consumer product",
"name": "ford",
"color": "red",
"description": "A car is a wheeled motor vehicle used for transportation."
"doors": "2",
"price": "$30000",
"milage": "100 miles"
},
"computer": {
"object_summary": {
"type": "hardware",
"name": "mac",
"color": "silver",
"description": "A computer is a device that can be instructed to carry out sequences of arithmetic or s for looms."
},
"purchase_date": "March 4, 2018",
"image": {
"url": "https://seniorsnoworlando.org/wp-content/uploads/2014/05/IMG_0009-1038x576.jpg",
"width": "50px",
"height": "50px"
}
},
"cat": {
"object_summary": {
"type": "animal",
"name": "Max",
"color": "orange",
"description": "The domestic cat carnivorous mammal."
},
"age": "2 years",
"favorite_toy": "ball",
"image": {
"url": "https://www.petful.com/wp-content/uploads/2016/06/american-shorthair-cat-750x434.jpg",
"width": "50px",
"height": "50px"
}
},
"dog": {
"object_summary": {
"type": "animal",
"name": "Jimmy",
"color": "black",
"description": "The domestic dog."
"age": "3 years",
"favorite_toy": "stuff animal",
"image": {
"url": "https://s3.amazonaws.com/cdn-origin-etr.akc.org/wp-content/uploads/2017/11/13000934/Beagle-On-White-08.jpg",
"width": "50px",
"height": "50px"
}
}
}
My data model looks like this:
struct DataModel: Codable{
let objectsummary:ObjectSummary
let doors,price, milage, purchasedate, age, favoritetoy: String
private enum CodingKeys: String, CodingKey {case purchasedate = "purchase_date", favoritetoy = "favorite_toy",objectsummary = "object_summary", doors, price, milage,age}
}
struct ObjectSummary:Codable{
let type: String
let name: String
let color: String
let description: String
}
1: You can construct the structs like you have with ObjectSummary (struct Car, struct Computer, etc..).
2: Have a 'root' struct that will include those as fields.
3: Finally, send that 'root' struct to the decoder.
I have one SwiftyJson object.
I can not filter that array. I have tried this solution https://stackoverflow.com/a/37497170/4831567. But my json format is different that's why not working.
[
{
"name": "19860",
"header": {
"start_time": "1519270200",
"end_time": "1519299000",
"state": "lunch"
},
"alerts": "1",
"venue": {
"location": "Delhi, India",
"timezone": "+05:30"
},
"srs_category": [
0,
1
]
},
{
"name": "19861",
"header": {
"start_time": "1519270200",
"end_time": "1519299000",
"state": "Dinner"
},
"alerts": "1",
"venue": {
"location": "Mumbai, India",
"timezone": "+05:30"
},
"srs_category": [
1,
3
]
},
{
"name": "19862",
"header": {
"start_time": "1519270200",
"end_time": "1519299000",
"state": "lunch"
},
"alerts": "1",
"venue": {
"location": "Surat, India",
"timezone": "+05:30"
},
"srs_category": [
0,
2
]
}
]
i want to find that object that srs_category contain 1. I know it is possible by looping and condition. But i want via NSPredicate. If it is possible then please help me.
Thank You.
Here is easy way to use SwiftyJSON:
let filtered = JSON(yourArray).arrayValue.filter({
$0["srs_category"].arrayValue.map({ $0.intValue }).contains(1)
})
Use a Swift native function rather than NSPredicate
data represents the Data object received from somewhere
do {
if let json = try JSONSerialization.jsonObject(with:data) as? [[String:Any]] {
let srsCategory1 = json.first(where: { dict -> Bool in
guard let array = dict["srs_category"] as? [Int] else { return false }
return array.contains(1)
})
print(srsCategory1 ?? "not found")
}
} catch {
print(error)
}
If there are multiple items which can match the condition replace first with filter. Then the result is a non-optional array.
I have a simple "rss" (ApplicationRecord) table indexed by an id. I would like to have a structured JSON that group each user from a family in an array structure. And then each family in a global array. How can I do that ?
my current plain code to put my data in a json file is :
json.rss #rss do |rs|
json.id rs.id
json.name rs.name
json.family rs.family
json.lastdate rs.lastdate
json.last rs.last
json.s1w rs.s1w
json.s2w rs.s2w
end
But the target file that I want is this one :
{
"rss": [
{
"familyname": "Smith",
"children": [
{
"id": "1",
"name": "bob",
"lastdate": "2010-09-23",
"last": "0.88",
"s1w": "0.83",
"s2w": "0.88"
},
{
"id": 2,
"name": "Mary",
"lastdate": "2011-09-23",
"last": "0.89",
"s1w": "0.83",
"s2w": "0.87"
}
]
},
{
"familyname": "Wesson",
"children": [
{
"id": "1",
"name": "john",
"lastdate": "2001-09-23",
"last": "0.88",
"s1w": "0.83",
"s2w": "0.88"
},
{
"id": 2,
"name": "Bruce",
"lastdate": "2000-09-23",
"last": "0.89",
"s1w": "0.83",
"s2w": "0.87"
}
]
}
]
}
The grouping you are trying to achieve can be done in Ruby with:
#rss.group_by(&:family).values
This is assuming #rss is an array-like collection of objects that have a .family method. The result: is an array of arrays of objects grouped by family.
Now it will be up to use to use Jbuilder's array! method to build the desired JSON output.