Swift: Can't seem to access objects in nested array - swift3

I am testing my feet in Swift and am trying to access certain objects which are part of a nested array.
The structure of the array looks something like this:
[ "Title": "Book Title", "Type": "Fiction", "Chapters": [
{"Title": "Chapter 1 Title", "Content": "Lorem ipsum dolor sit amet...", "Keyword": "foo"},
{"Title": "Chapter 3 Title", "Content": "Lorem ipsum dolor sit amet...", "Keyword": "foo"},
{"Title": "Chapter 5 Title", "Content": "Lorem ipsum dolor sit amet...", "Keyword": "foo"},
...
],
[ "Title": "Book Title", "Type": "Fiction", "Chapters": [
{"Title": "Chapter 2 Title", "Content": "Lorem ipsum dolor sit amet...", "Keyword": "foo"},
{"Title": "Chapter 3 Title", "Content": "Lorem ipsum dolor sit amet...", "Keyword": "foo"}
],
...]
The array is loaded from a plist file and is loaded something like this:
var myArr: Array<Any>
myArr = NSArray(contentsOfFile: path!) as! [Dictionary<String, Any>]
I am having difficulty accessing specific children of the chapters array.
let bookNum = 1
let chapNum = 3
let s: Dictionary = myArr[bookNum] as! Dictionary<String, Any>
if let s1 = (s["Chapters"] as AnyObject)[chapNum] {
if let obj = s1 as! Dictionary<String, Any> {
print("/(obj["Title"])")
}
}
I get the error, that "Initializer for conditional binding must have optional type, not Dictionary".
In general I am getting the feeling that I am not doing this correctly. The problem is, that the exact structure of the array is not known. Theoretically a Book could be listed without chapters.
Thanks for help in advance.

You have already specified the myArr type as [Dictionary<String, Any>], so there is no need to explicitly specify the type of s because if you access object from myArr it should only dictionary nothing else.
let s = myArr[bookNum]
Now Chapters is also Array of dictionary so you need to type cast it that and then access the chapter according to you want.
if let chapters = s["Chapters"] as? [Dictionary<String, Any>] {// OR [[String:Any]]
if chapNum < chapters.count {
print(chapters[chapNum]["Title"])
}
}

Related

How do I extract data from "List" field

I'm getting JSON data from webservice and trying to make a table . Datadisk is presented as List and clicking into each item will navigate further down the hiearchy like denoted in screenshots below. I need to concatate storageAccountType for each item with | sign, so if there were 2 list items for Greg-VM and it had Standard_LRS for first one and Premium_LRS for second one then new column will list Standard_LRS | Premium_LRS for that row.
Input returned by function is below
[
{
"name": "rhazuremspdemo",
"disk": {
"id": "/subscriptions/24ba3e4c-45e3-4d55-8132-6731cf25547f/resourceGroups/AzureMSPDemo/providers/Microsoft.Compute/disks/rhazuremspdemo_OsDisk_1_346353b875794dd4a7a5c5938abfb7df",
"storageAccountType": "StandardSSD_LRS"
},
"datadisk": []
},
{
"name": "w12azuremspdemo",
"disk": {
"id": "/subscriptions/24ba3e4c-45e3-4d55-8132-6731cf25547f/resourceGroups/AzureMSPDemo/providers/Microsoft.Compute/disks/w12azuremspdemo_OsDisk_1_09788205f8eb429faa082866ffee0f18",
"storageAccountType": "Premium_LRS"
},
"datadisk": []
},
{
"name": "Greg-VM",
"disk": {
"id": "/subscriptions/24ba3e4c-45e3-4d55-8132-6731cf25547f/resourceGroups/GREG/providers/Microsoft.Compute/disks/Greg-VM_OsDisk_1_63ed471fef3e4f568314dfa56ebac5d2",
"storageAccountType": "Premium_LRS"
},
"datadisk": [
{
"name": "Data",
"createOption": "Attach",
"diskSizeGB": 10,
"managedDisk": {
"id": "/subscriptions/24ba3e4c-45e3-4d55-8132-6731cf25547f/resourceGroups/GREG/providers/Microsoft.Compute/disks/Data",
"storageAccountType": "Standard_LRS"
},
"caching": "None",
"toBeDetached": false,
"lun": 0
},
{
"name": "Disk2",
"createOption": "Attach",
"diskSizeGB": 10,
"managedDisk": {
"id": "/subscriptions/24ba3e4c-45e3-4d55-8132-6731cf25547f/resourceGroups/GREG/providers/Microsoft.Compute/disks/Disk2",
"storageAccountType": "Standard_LRS"
},
"caching": "None",
"toBeDetached": false,
"lun": 1
}
]
}
]
How do I do that?
Thanks,
G
This should help you. It steps through the process.
If you have a scenario like this
you can use Add custom Column and type the follwing expression:
=Table.Columns([TableName], "ColumnName")
to get it as list:
Now you can left click on the Custom column and chose Extract Values....
Choose Custom and your delimiter | and hit OK
This way the data who was in your list will now be in the same row with the delimiter

jq 1.5 find nested elements by regex when parent unknown

Given the JSON structure below i would like to find the first occurrence of object ccc so I can add a new object to the children ddd. However I do not know the key name of the parent or how many levels deep it may be.
to find
"children": {
"ccc": [{
"id": "ddd",
"des": "object d",
"parent": "ccc"
}]
}
full JSON stored in $myJson
{
"zzz": [{
"id": "aaa",
"des": "object A",
"parent": "zzz",
"children": {
"aaa": [{
"id": "bbb",
"des": "object B",
"parent": "aaa",
"children": {
"bbb": [{
"id": "ccc",
"des": "object C",
"parent": "bbb",
"children": {
"ccc": [{
"id": "ddd",
"des": "object d",
"parent": "ccc"
}]
}
}, {
"id": "eee",
"des": "object e",
"parent": "bbb"
}]
}
},{
"id": "fff",
"des": "object f",
"parent": "aaa"
}]
}
}]}
follow some other answers I have tried combinations of
output=($(jq -r '.. | with_entries(select(.key|match("ccc";"i")))' <<< ${myjson}))
or
output=($(jq -r '.. | to_entries | map(select(.key | match("ccc";"i"))) | map(.value)' <<< ${myjson}))
all give errors of a similar nature jq: error (at <stdin>:1): number (0) cannot be matched, as it is not a string
In the following, I'll assume you want to add "ADDITIONAL" to the array at EVERY key that matches a given regex (here "ccc"):
walk(if type == "object"
then with_entries(if (.key|test("ccc"))
then .value += ["ADDITIONAL"] else . end)
else . end)
If your jq does not have walk/1, then you can simply copy-and-paste its def from the jq FAQ or builtin.jq
Alternative formulation
If you have the following general-purpose helper function handy (e.g. in your ~/.jq):
def when(filter; action): if (filter?) // null then action else . end;
then the above solution shrinks down to:
walk(when(type == "object";
with_entries(when(.key|test("ccc"); .value += ["ADDITIONAL"]))))

How to display nested related model data in Ember

To preface this, I'm new to Ember and using Mirage to mock a JSON-API compliant backend, but I've hit a snag on what I think would be a common scenario. Ideally, I'm looking to create a single view that lists posts and the comments for each post underneath. The trouble comes when I want to display the author associated with each comment. So, I must clearly be doing something wrong here, since Ember knows how to fetch the direct associations for the Post model, but anything deeper than that is undefined.
In my route, I fetch all posts and that knows to then request the relationship data from the proper Mirage routes.
// app/routes/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.store.findAll('post');
}
});
This is the response that Ember receives from Mirage when requesting all posts.
{
"data": [
{
"type": "posts",
"id": "1",
"attributes": {
"title": "Vero quas non inventore eos vel rerum nesciunt nemo molestiae.",
"body": "Eum minima beatae ullam nam id ut quia.\nPorro quidem blanditiis provident qui ex voluptas temporibus officia quos.\nDeleniti aut soluta placeat illo.\nId aut dolorem illo fugit corrupti commodi.\nPorro nesciunt enim debitis.\nMinima architecto velit corporis excepturi eos qui.",
},
"relationships": {
"author": {
"data": {
"type": "users",
"id": "10"
}
},
"comments": {
"data": []
}
}
},
{
"type": "posts",
"id": "2",
"attributes": {
"title": "Id quae est omnis dolorum quaerat aut sed corrupti voluptatem.",
"body": "Est ipsa voluptas quia quae nihil ipsum assumenda itaque nihil.\nTotam aut quia.\nRerum maxime cum distinctio harum dolorem dolores dicta.\nNesciunt id et minima velit omnis eius itaque ad.",
},
"relationships": {
"author": {
"data": {
"type": "users",
"id": "1"
}
},
"comments": {
"data": []
}
}
},
{
"type": "posts",
"id": "3",
"attributes": {
"title": "Provident et eius est.",
"body": "Neque autem deserunt.\nAb repellendus nemo et aut sunt veritatis facere asperiores soluta.\nEt placeat id dicta sint.\nHarum temporibus eos labore.",
},
"relationships": {
"author": {
"data": {
"type": "users",
"id": "8"
}
},
"comments": {
"data": []
}
}
},
{
"type": "posts",
"id": "4",
"attributes": {
"title": "A similique explicabo itaque dolor vel possimus aut praesentium veritatis.",
"body": "Inventore et ipsum ut porro.\nUt sed est unde illo nulla id doloribus accusamus voluptatum.\nTempora officiis ut enim porro et est qui.\nSit qui minima iste eaque cupiditate molestiae ut omnis magni.",
},
"relationships": {
"author": {
"data": {
"type": "users",
"id": "4"
}
},
"comments": {
"data": []
}
}
},
{
"type": "posts",
"id": "5",
"attributes": {
"title": "Et in consequatur ut autem et.",
"body": "Qui voluptatem harum aut amet possimus architecto eos commodi.\nNumquam cupiditate fugit.\nQuod consequatur minima aspernatur nobis qui eligendi qui corporis necessitatibus.\nIste velit perferendis non dolore ipsum perspiciatis quia.\nAut delectus et porro cupiditate laboriosam dolorem.\nEaque ipsa rerum ipsam placeat voluptatem enim.",
},
"relationships": {
"author": {
"data": {
"type": "users",
"id": "1"
}
},
"comments": {
"data": [
{
"type": "comments",
"id": "4"
}
]
}
}
},
{
"type": "posts",
"id": "6",
"attributes": {
"title": "Exercitationem quo perferendis.",
"body": "Dolor ut voluptates placeat ullam.\nOmnis aut et.\nIste est tenetur deleniti ea incidunt eos voluptas veniam iusto.",
},
"relationships": {
"author": {
"data": {
"type": "users",
"id": "3"
}
},
"comments": {
"data": [
{
"type": "comments",
"id": "1"
},
{
"type": "comments",
"id": "5"
},
{
"type": "comments",
"id": "9"
}
]
}
}
},
{
"type": "posts",
"id": "7",
"attributes": {
"title": "Officia ea quod natus corrupti.",
"body": "Et quia qui occaecati aspernatur voluptatem error in.\nDoloremque rerum sed autem minima quidem reiciendis.\nPossimus dolores voluptas voluptate rerum veniam dicta.\nNemo dolore perspiciatis harum dolorem soluta ab consectetur animi sed.",
},
"relationships": {
"author": {
"data": {
"type": "users",
"id": "1"
}
},
"comments": {
"data": [
{
"type": "comments",
"id": "3"
}
]
}
}
},
{
"type": "posts",
"id": "8",
"attributes": {
"title": "Quia ea cum vel repudiandae.",
"body": "Excepturi dolores sed modi est asperiores deleniti.\nTempore architecto recusandae nostrum culpa expedita iure voluptatibus accusantium nemo.\nQuia est voluptatum nulla earum culpa.",
},
"relationships": {
"author": {
"data": {
"type": "users",
"id": "7"
}
},
"comments": {
"data": [
{
"type": "comments",
"id": "2"
},
{
"type": "comments",
"id": "7"
},
{
"type": "comments",
"id": "8"
}
]
}
}
},
{
"type": "posts",
"id": "9",
"attributes": {
"title": "Nam fugit in voluptatibus et.",
"body": "Aut nihil atque tempore beatae voluptas.\nOptio voluptatum qui debitis omnis dolor maiores cumque.\nUt dolorem est magnam eveniet.\nMagni porro occaecati ex autem.\nPorro et alias beatae nemo laboriosam ut sint magnam quis.\nMollitia deserunt culpa non.",
},
"relationships": {
"author": {
"data": {
"type": "users",
"id": "9"
}
},
"comments": {
"data": [
{
"type": "comments",
"id": "10"
}
]
}
}
},
{
"type": "posts",
"id": "10",
"attributes": {
"title": "Aut delectus nobis voluptate.",
"body": "Alias impedit itaque at rerum enim.\nVoluptas itaque quaerat qui optio quo.\nNihil voluptatem quos nihil pariatur sapiente tempore necessitatibus quia et.\nSed consectetur modi dolorum sunt ex odit at.\nVoluptas numquam totam dolores ipsam rerum.\nEt hic eum sunt et.",
},
"relationships": {
"author": {
"data": {
"type": "users",
"id": "1"
}
},
"comments": {
"data": [
{
"type": "comments",
"id": "6"
}
]
}
}
}
]
}
After getting all posts and the top-level relation models, Ember doesn't go any deeper so I'm left with an undefined comment.author in my template. Is there some way I can tell Ember to fetch the nested models that I need or am I going about this all wrong?
EDIT
This is the structure of my models and the templates for them. Hopefully, it will help give some more context.
Post model:
// app/models/post.js
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string'),
body: DS.attr('string'),
createdAt: DS.attr('date', {
defaultValue() { return new Date(); }
}),
author: DS.belongsTo('user'),
comments: DS.hasMany('comment')
});
Comment model:
// app/models/comment.js
import DS from 'ember-data';
export default DS.Model.extend({
body: DS.attr('string'),
createdAt: DS.attr('data', {
defaultValue() { return new Date(); }
}),
author: DS.belongsTo('user'),
post: DS.belongsTo('post')
});
User model:
// app/models/user.js
import DS from 'ember-data';
export default DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
posts: DS.hasMany('post'),
comments: DS.hasMany('comment'),
fullName: Ember.computed('firstName', 'lastName', function() {
return `${this.get('firstName')} ${this.get('lastName')}`;
})
});
Index template:
// app/templates/index.hbs
{{#each model as |post|}}
{{post-content post=post}}
{{/each}}
Post template:
// app/templates/components/post-content.hbs
<div class="post-main">
<h5 class="post-author-name">{{post.author.fullName}}</h5>
<div class="post-timestamp">
<h5 class="time-in-words">{{moment-from-now post.createdAt interval=1000}}</h5>
</div>
<div class="post-content">
<h2 class="post-title">{{post.title}}</h2>
<p class="post-body">{{post.body}}</p>
</div>
<div class="post-actions">
<h6>Comments ({{post.comments.length}})</h6>
</div>
</div>
<ul class="post-comments">
{{#each post.comments as |comment|}}
{{post-comment comment=comment}}
{{/each}}
</ul>
Comment template:
// app/templates/components/post-comment.hbs
<!-- This is blank -->
<h5>{{comment.author.fullName}}</h5>
<!-- This is not blank -->
<p>{{comment.body}}</p>

How do you handle large relationship data attributes and compound documents?

If an article has several comments (think thousands over time). Should data.relationships.comments return with a limit?
{
"data": [
{
"type": "articles",
"id": 1,
"attributes": {
"title": "Some title",
},
"relationships": {
"comments": {
"links": {
"related": "https://www.foo.com/api/v1/articles/1/comments"
},
"data": [
{ "type": "comment", "id": "1" }
...
{ "type": "comment", "id": "2000" }
]
}
}
}
],
"included": [
{
"type": "comments",
"id": 1,
"attributes": {
"body": "Lorem ipusm",
}
},
.....
{
"type": "comments",
"id": 2000,
"attributes": {
"body": "Lorem ipusm",
}
},
]
}
This starts to feel concerning, when you think of compound documents (http://jsonapi.org/format/#document-compound-documents). Which means, the included section will list all comments as well, making the JSON payload quite large.
If you want to limit the number of records you get at a time from a long list use pagination (JSON API spec).
I would load the comments separately with store.query (ember docs), like so -
store.query('comments', { author_id: <author_id>, page: 3 });
which will return the relevant subset of comments.
If you don't initially want to make two requests per author, you could include the first 'page' in the authors request as you're doing now.
You may also want to look into an addon like Ember Infinity (untested), which will provide an infinite scrolling list and automatically make pagination requests.

Couchdb document/function

In a document with the following information:
{
"address": [{
"Street": "123 xyz",
"City": "Belmont"
}]
}
How can I view the name of the cities. Is this correct:
function(doc) {
emit(doc.address.City,null);
}
It returns only null. I wanted to see the name "Belmont".
Any help with be appreciated.
In your data, address is an array, so it does not have a City property.
If you only have one address in your data:
{
"address": {
"Street": "123 xyz",
"City": "Belmont"
}
}
Getting /{database}/_design/{ddoc}/_view/{view} should return:
{"rows":[
{"key":"Belmont", "id":"{id}", "value":null}
]}
As a side note, please note that you can also get /{database}/_design/{ddoc}/_view/{view}?include_docs=true:
{"rows":[
{"key":"Belmont", "id":"{id}", "value":null, "doc":{
"address": {
"Street": "123 xyz",
"City": "Belmont"
}
}}
]}
Last but not least, if you really need multiple adresses in your data, you can send them all:
function(o) {
for each (var a in o.address) {
emit(a.City);
}
}