Convert array string to array in Swift [duplicate] - ios

I have the following Json response.
{
"language": "en",
"textAngle": 0,
"orientation": "Up",
"regions": [
{
"boundingBox": "96,29,244,474",
"lines": [
{
"boundingBox": "96,29,58,12",
"words": [
{
"boundingBox": "96,29,58,12",
"text": "SG4207"
}
]
},
{
"boundingBox": "97,64,159,16",
"words": [
{
"boundingBox": "97,65,27,15",
"text": "Eng"
},
{
"boundingBox": "129,64,34,16",
"text": "Lieh,"
},
{
"boundingBox": "168,65,37,12",
"text": "Yuen"
},
{
"boundingBox": "212,65,44,15",
"text": "Kwan,"
}
]
},
{
"boundingBox": "97,99,243,16",
"words": [
{
"boundingBox": "97,99,52,13",
"text": "Mobile"
},
{
"boundingBox": "154,99,64,13",
"text": "Wireless"
},
{
"boundingBox": "223,99,62,13",
"text": "Solution"
},
{
"boundingBox": "291,99,49,16",
"text": "Design"
}
]
},
{
"boundingBox": "97,134,117,16",
"words": [
{
"boundingBox": "97,134,44,16",
"text": "Darryl"
},
{
"boundingBox": "147,134,27,13",
"text": "and"
},
{
"boundingBox": "179,134,35,16",
"text": "Ajith"
}
]
},
{
"boundingBox": "96,169,71,16",
"words": [
{
"boundingBox": "96,169,71,16",
"text": "Weekday"
}
]
},
{
"boundingBox": "97,205,72,16",
"words": [
{
"boundingBox": "97,205,72,16",
"text": "(Monday)"
}
]
},
{
"boundingBox": "96,241,80,15",
"words": [
{
"boundingBox": "96,244,32,12",
"text": "gam"
},
{
"boundingBox": "133,248,5,1",
"text": "-"
},
{
"boundingBox": "143,241,33,15",
"text": "5pm"
}
]
},
{
"boundingBox": "96,275,72,13",
"words": [
{
"boundingBox": "96,275,72,13",
"text": "Weekend"
}
]
},
{
"boundingBox": "97,310,77,16",
"words": [
{
"boundingBox": "97,310,77,16",
"text": "(Saturday)"
}
]
},
{
"boundingBox": "96,347,80,15",
"words": [
{
"boundingBox": "96,350,32,12",
"text": "gam"
},
{
"boundingBox": "133,354,5,1",
"text": "-"
},
{
"boundingBox": "143,347,33,15",
"text": "5pm"
}
]
},
{
"boundingBox": "97,382,41,15",
"words": [
{
"boundingBox": "97,382,41,15",
"text": "3-Apr"
}
]
},
{
"boundingBox": "97,417,45,15",
"words": [
{
"boundingBox": "97,417,8,12",
"text": "1"
},
{
"boundingBox": "115,417,27,15",
"text": "Apr"
}
]
},
{
"boundingBox": "97,452,48,15",
"words": [
{
"boundingBox": "97,452,48,15",
"text": "ID-Apr"
}
]
},
{
"boundingBox": "96,488,42,15",
"words": [
{
"boundingBox": "96,488,42,15",
"text": "8-Apr"
}
]
}
]
}
]
}
Need to create a String array from the above json in Swift3. Tried the following
if dictionary["regions"] != nil {
// Get Regions from the dictionary
let regions = (dictionary["regions"] as! NSArray).firstObject as? [String:AnyObject]
// Get lines from the regions dictionary
let lines = regions!["lines"] as! NSArray
// Get words from lines
let inLine = lines.enumerated().map {($0.element as? NSDictionary)?["words"] as! [[String : AnyObject]] }
// Get text from words
let extractedText = inLine.enumerated().map { $0.element[0]["text"] as! String}
return extractedText
} else {
return [""];
}
But did not get the actual String.

You just need to cast to the correct types, your code is almost correct. I created this code in a playground and I can get all of the values.
let jsonString = "{\"language\":\"en\",\"textAngle\":0,\"orientation\":\"Up\",\"regions\":[{\"boundingBox\":\"96,29,244,474\",\"lines\":[{\"boundingBox\":\"96,29,58,12\",\"words\":[{\"boundingBox\":\"96,29,58,12\",\"text\":\"SG4207\"}]},{\"boundingBox\":\"97,64,159,16\",\"words\":[{\"boundingBox\":\"97,65,27,15\",\"text\":\"Eng\"},{\"boundingBox\":\"129,64,34,16\",\"text\":\"Lieh,\"},{\"boundingBox\":\"168,65,37,12\",\"text\":\"Yuen\"},{\"boundingBox\":\"212,65,44,15\",\"text\":\"Kwan,\"}]},{\"boundingBox\":\"97,99,243,16\",\"words\":[{\"boundingBox\":\"97,99,52,13\",\"text\":\"Mobile\"},{\"boundingBox\":\"154,99,64,13\",\"text\":\"Wireless\"},{\"boundingBox\":\"223,99,62,13\",\"text\":\"Solution\"},{\"boundingBox\":\"291,99,49,16\",\"text\":\"Design\"}]},{\"boundingBox\":\"97,134,117,16\",\"words\":[{\"boundingBox\":\"97,134,44,16\",\"text\":\"Darryl\"},{\"boundingBox\":\"147,134,27,13\",\"text\":\"and\"},{\"boundingBox\":\"179,134,35,16\",\"text\":\"Ajith\"}]},{\"boundingBox\":\"96,169,71,16\",\"words\":[{\"boundingBox\":\"96,169,71,16\",\"text\":\"Weekday\"}]},{\"boundingBox\":\"97,205,72,16\",\"words\":[{\"boundingBox\":\"97,205,72,16\",\"text\":\"(Monday)\"}]},{\"boundingBox\":\"96,241,80,15\",\"words\":[{\"boundingBox\":\"96,244,32,12\",\"text\":\"gam\"},{\"boundingBox\":\"133,248,5,1\",\"text\":\"-\"},{\"boundingBox\":\"143,241,33,15\",\"text\":\"5pm\"}]},{\"boundingBox\":\"96,275,72,13\",\"words\":[{\"boundingBox\":\"96,275,72,13\",\"text\":\"Weekend\"}]},{\"boundingBox\":\"97,310,77,16\",\"words\":[{\"boundingBox\":\"97,310,77,16\",\"text\":\"(Saturday)\"}]},{\"boundingBox\":\"96,347,80,15\",\"words\":[{\"boundingBox\":\"96,350,32,12\",\"text\":\"gam\"},{\"boundingBox\":\"133,354,5,1\",\"text\":\"-\"},{\"boundingBox\":\"143,347,33,15\",\"text\":\"5pm\"}]},{\"boundingBox\":\"97,382,41,15\",\"words\":[{\"boundingBox\":\"97,382,41,15\",\"text\":\"3-Apr\"}]},{\"boundingBox\":\"97,417,45,15\",\"words\":[{\"boundingBox\":\"97,417,8,12\",\"text\":\"1\"},{\"boundingBox\":\"115,417,27,15\",\"text\":\"Apr\"}]},{\"boundingBox\":\"97,452,48,15\",\"words\":[{\"boundingBox\":\"97,452,48,15\",\"text\":\"ID-Apr\"}]},{\"boundingBox\":\"96,488,42,15\",\"words\":[{\"boundingBox\":\"96,488,42,15\",\"text\":\"8-Apr\"}]}]}]}"
if let jsonDict = (try? JSONSerialization.jsonObject(with: Data(jsonString.utf8))) as? [String: Any] {
if let regions = jsonDict["regions"] as? [[String: Any]] {
for region in regions {
if let lines = region["lines"] as? [[String: Any]] {
for line in lines {
if let words = line["words"] as? [[String: Any]] {
for word in words {
if let text = word["text"] {
print(text)
}
}
}
}
}
}
}
}
Output:
Weekday
(Monday)
gam
-
5pm
Weekend
(Saturday)
gam
-
5pm
3-Apr
1
Apr
ID-Apr
8-Apr
Obviously there's alot of nesting here, you could tidy it up and shorten it using functions such as map, but this exercise was more to show you the casting that you need for each level.

Related

Swift Nested jSON Decode

I have problem with nested json decoding. Im getting no error but response is empty { }. Down bellow is my sample json and struct.
{
"categories": [
{
"ID": 130,
"data": [
{
"en": [
{
"title": "test"
}
],
"fr": [
{
"title": "teste"
}
]
}
],
"lifts": [
{
"ID": 104,
"data": [
{
"en": [
{
"code": "test",
"title": "test"
}
],
"fr": [
{
"code": "test",
"title": "test"
}
]
}
]
},
{
"ID": 105,
"data": [
{
"en": [
{
"code": "test",
"title": "test"
}
],
"fr": [
{
"code": "test",
"title": "test"
}
]
}
]
}
]
}
And this is my struct
struct jsonResponse : Codable {
struct Categories : Codable {
let id : Int
let data : [LanguageData]
let lifts : [Lifts]
struct LanguageData : Codable {
let en, fr : [Data]
struct Data : Codable {
let code : String?
let title : String?
}
}
struct LiftsData : Codable {
let id : Int
let data : [LanguageData]
}
}
Then Im trying to decode JSON like this:
let lifts = try JSONDecoder().decode(jsonResponse.self, from: data)
But when I print lifts, i see only empty {}. Also no error message during decoding, so have no idea what can be wrong.

How to parse particular JSON in Swift

To parse a JSON, as I found also on the web, I usually used this kind of code:
guard let results = receivedUserJSON["results"] as? [String: Any] else {
print("Error interpreting results")
return
}
This time I have a problem, because it seems to end in the else of this guard let. The JSON has the following structure:
{
"results": [{
"gender": "female",
"name": {
"title": "mrs",
"first": "silene",
"last": "almeida"
},
"location": {
"street": "2594 rua maranhão ",
"city": "pouso alegre",
"state": "distrito federal",
"postcode": 20447,
"coordinates": {
"latitude": "-70.0198",
"longitude": "123.6577"
},
"timezone": {
"offset": "+4:30",
"description": "Kabul"
}
},
"email": "silene.almeida#example.com",
"login": {
"uuid": "d06a46b3-1c00-42be-b8fc-d271bf901f7d",
"username": "silversnake251",
"password": "ventura",
"salt": "UcckU6RG",
"md5": "7c8c4129587c61da01ca7cf4f88353c5",
"sha1": "6cbf7ec377ff4ebad5a392ec487343bf613858ef",
"sha256": "8dedf3649fb833a1936b8885627b86c6cf02062eb74f727b2cbd674a30f73e75"
},
"dob": {
"date": "1969-07-13T00:58:26Z",
"age": 49
},
"registered": {
"date": "2003-09-28T09:44:56Z",
"age": 15
},
"phone": "(95) 0094-8716",
"cell": "(20) 1014-3529",
"id": {
"name": "",
"value": null
},
"picture": {
"large": "https://randomuser.me/api/portraits/women/66.jpg",
"medium": "https://randomuser.me/api/portraits/med/women/66.jpg",
"thumbnail": "https://randomuser.me/api/portraits/thumb/women/66.jpg"
},
"nat": "BR"
}],
"info": {
"seed": "dd971cddf636d2d7",
"results": 1,
"page": 1,
"version": "1.2"
}
}
What should I do to properly parse this JSON? I would prefer not to go for the Codable solution because I don't need all of these values.
PS: I know the json is correct because I tried and printed it with:
if let JSONString = String(data: responseData, encoding: String.Encoding.utf8) {
print(JSONString)
}
results is an array
guard let results = receivedUserJSON["results"] as? [[String:Any]] else {
print("Error interpreting results")
return
}
I see no value for it to be an array as it contains 1 element so you may think to alter this json
current strucsture
{
"results": [{}],
"info": {
"seed": "dd971cddf636d2d7",
"results": 1,
"page": 1,
"version": "1.2"
}
}
you may alter it to
{
"results": {},
"info": {
"seed": "dd971cddf636d2d7",
"results": 1,
"page": 1,
"version": "1.2"
}
}

Combine multiple has_child to single has_child Elasticsearch

I have a type in elasticsearch type_1 in which it has only two fields name and value.
I have following elasticsearch query, Is there any alternative of below query that produce same result using only one has_child ?
GET /my_index/my_type/_search
{
"query": {
"bool": {
"must": [
{
"has_child": {
"type": "type_1",
"query": {
"bool": {
"must": [
{
"term": {
"name": "field_1"
}
},
{
"term": {
"value": "val1"
}
}
]
}
}
}
},
{
"has_child": {
"type": "type_1",
"query": {
"bool": {
"must": [
{
"term": {
"name": "field_2"
}
},
{
"term": {
"value": "val2"
}
}
]
}
}
}
}
]
}
}
}

Searchkick Aggregations behavior

I am working with the Searchkick Gem and Elastic search and am trying to understand the aggregations behavior.
I have three facets (Aggregations): City, State and Company.
If I filter by any one of them, the counts of other two are reduced to reflect the total in the result set. But the selected facet comes back with all values. So say I had 100 items in the index, and I filtered by a Company that had 2 total items in the index, the City and State counts are updated to reflect no more than 2. But the Company count remains at 100.
Example (filtered to City=Atlanta)
{
"query": {
"function_score": {
"functions": [
{
"filter": {
"and": [
{
"term": {
"featured": true
}
}
]
},
"boost_factor": 1000
}
],
"query": {
"match_all": {}
},
"score_mode": "sum"
}
},
"size": 20,
"from": 0,
"post_filter": {
"bool": {
"filter": [
{
"range": {
"expiration_date": {
"from": "2016-08-18T23:07:15.670-04:00",
"include_lower": true
}
}
},
{
"range": {
"created_at": {
"to": "2016-08-18T23:07:15.670-04:00",
"include_upper": true
}
}
},
{
"term": {
"published": true
}
},
{
"term": {
"tenant_id": 4
}
},
{
"term": {
"city": "Atlanta"
}
}
]
}
},
"aggs": {
"company": {
"filter": {
"bool": {
"must": [
{
"range": {
"expiration_date": {
"from": "2016-08-18T23:07:15.670-04:00",
"include_lower": true
}
}
},
{
"range": {
"created_at": {
"to": "2016-08-18T23:07:15.670-04:00",
"include_upper": true
}
}
},
{
"term": {
"published": true
}
},
{
"term": {
"tenant_id": 4
}
},
{
"term": {
"city": "Atlanta"
}
}
]
}
},
"aggs": {
"company": {
"terms": {
"field": "company",
"size": 10
}
}
}
},
"city": {
"filter": {
"bool": {
"must": [
{
"range": {
"expiration_date": {
"from": "2016-08-18T23:07:15.670-04:00",
"include_lower": true
}
}
},
{
"range": {
"created_at": {
"to": "2016-08-18T23:07:15.670-04:00",
"include_upper": true
}
}
},
{
"term": {
"published": true
}
},
{
"term": {
"tenant_id": 4
}
}
]
}
},
"aggs": {
"city": {
"terms": {
"field": "city",
"size": 10
}
}
}
},
"state": {
"filter": {
"bool": {
"must": [
{
"range": {
"expiration_date": {
"from": "2016-08-18T23:07:15.670-04:00",
"include_lower": true
}
}
},
{
"range": {
"created_at": {
"to": "2016-08-18T23:07:15.670-04:00",
"include_upper": true
}
}
},
{
"term": {
"published": true
}
},
{
"term": {
"tenant_id": 4
}
},
{
"term": {
"city": "Atlanta"
}
}
]
}
},
"aggs": {
"state": {
"terms": {
"field": "state",
"size": 10
}
}
}
}
},
"fields": []
}
Result (2 result returned, but 58 City Aggregations come back). Note Company and City return correct # of Aggregations:
{
"took": 114,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1,
"hits": [
{
"_index": "jobs_development_20160818140128648",
"_type": "job",
"_id": "457134",
"_score": 1
},
{
"_index": "jobs_development_20160818140128648",
"_type": "job",
"_id": "457137",
"_score": 1
}
]
},
"aggregations": {
"city": {
"doc_count": 58,
"city": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 19,
"buckets": [
{
"key": "Los Angeles",
"doc_count": 8
},
{
"key": "London",
"doc_count": 7
},
{
"key": "New York",
"doc_count": 7
},
{
"key": "Burbank",
"doc_count": 5
},
{
"key": "Pasig",
"doc_count": 3
},
{
"key": "Atlanta",
"doc_count": 2
},
{
"key": "Chicago",
"doc_count": 2
},
{
"key": "Culver City",
"doc_count": 2
},
{
"key": "London Borough of Hackney",
"doc_count": 2
},
{
"key": "Birmingham",
"doc_count": 1
}
]
}
},
"company": {
"doc_count": 2,
"company": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Second Story",
"doc_count": 2
}
]
}
},
"state": {
"doc_count": 2,
"state": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Georgia",
"doc_count": 2
}
]
}
}
}
}
What am I missing? Is this correct behavior?

Elasticsearch - using the path hierarchy tokenizer to access different level of categories

I'm very new in Elasticsearch and have a question about the hierarchical tokenizer of a path. Here is my code example:
My mapping code:
PUT /my_index
{
"settings": {
"analysis": {
"analyzer": {
"path-analyzer": {
"type": "custom",
"tokenizer": "path-tokenizer"
}
},
"tokenizer": {
"path-tokenizer": {
"type": "path_hierarchy",
"delimiter": "."
}
}
}
},
"mappings": {
"my_type": {
"dynamic": "strict",
"properties": {
"group_path": {
"type": "string",
"index_analyzer": "path-analyzer",
"search_analyzer": "keyword"
}
}
}
}
}
This is my PUT:
PUT /my_index/my_type/1
{
"group_path": ["Book.Thriller.Adult","DVD.Comedy.Kids"]
}
This is my Query:
GET /my_index/my_type/_search?search_type=count
{
"aggs": {
"category": {
"terms": {
"field": "group_path",
"size": 0
}
}
}
}
And the result:
{
...
"aggregations": {
"category": {
"buckets": [
{
"key": "Book",
"doc_count": 1
},
{
"key": "Book.Thriller",
"doc_count": 1
},
{
"key": "Book.Thriller.Adult",
"doc_count": 1
},
{
"key": "DVD",
"doc_count": 1
},
{
"key": "DVD.Comedy",
"doc_count": 1
},
{
"key": "DVD.Comedy.Kids",
"doc_count": 1
}
]
}
}
}
So far is everything good. What I'm looking for is that how can I create buckets for example only for the first category. How can I get result like that:
{
...
"aggregations": {
"category": {
"buckets": [
{
"key": "Book",
"doc_count": 1
},
{
"key": "DVD",
"doc_count": 1
}
]
}
}
}
Thank you for any help.
The only way I found to do this is to use the exclude syntax to exclude the levels you don't want.
{
"aggs": {
"category": {
"terms": {
"field": "group_path",
"size": 0,
"exclude" : ".*\\..*"
}
}
}
}
Will then return
aggregations: {
category: {
buckets: [
{
key: Book
doc_count: 1
}
{
key: DVD
doc_count: 1
}
]
}
}
If you select book, you can then search like this
{
"query" : {
"filtered": {
"filter": {
"prefix": {
"group_path": "Book"
}
}
}
},
"aggs" : {
"category": {
"terms": {
"field": "group_path",
"size": 0,
"include" : "Book\\..*",
"exclude": ".*\\..*\\..*"
}
}
}
}
Will then return
aggregations: {
category: {
buckets: [
{
key: Book.Thriller
doc_count: 1
}
]
}
}

Resources