Dictionary print key and value in Swift 4 [duplicate] - swift

This question already has an answer here:
From which direction swift starts to read dictionaries? [duplicate]
(1 answer)
Closed 5 years ago.
Hello I tried a print dictionary items at Xcode9. Like this:
var items = ["Bear":"0", "Glass":"1", "Car":"2"]
for (key,value) in items{
print("\(key) : \(value)")
}
output:
Glass : 1
Bear : 0
Car : 2
Why output not like this: Bear: 0, Glass:1, Car:2
I dont understand this output reason.

Dictionary :
A dictionary stores associations between keys of the same type and values of the same type in a collection with no defined ordering.
Each value is associated with a unique key, which acts as an identifier for that value within the dictionary. Unlike items in an array, items in a dictionary do not have a specified order.
Array - An array stores values of the same type in an ordered list.
Sets - A set stores distinct values of the same type in a collection with no defined ordering.
From Apple documentation

Dictionary in Swift is implemented as hash map. There is no guarantee that items in Dictionary will have the same order you added them.
The only container that retains the order of items is Array. You can use it to store tuples, like so:
var items : [(key: String, value: String)] = [(key: "Bear", value: "0"),(key: "Glass", value: "1"), (key: "Car", value: "2")]
Your iteration will work as expected, but you will lose Dictionary's ability to lookup items by subscript

Arrays are ordered collections of values.
Sets are unordered collections of unique values.
Dictionaries are unordered
collections of key-value associations.
So, You can not expect the same order, when you are iterating the values from Dictionary.
Reference to Collection Type

Dictionary collection isn't ordered, that is it simply doesn't guarantee to be ordered. But Array is. Dictionary adds values in discrete orders whereas Array adds values in continuous order.
You simply can't have an Array like this:
["a", "b", ... , "d", ... , ... , "g"] //Discrete index aren't allowed. You just can't skip any index in between.
Instead you have to have the above array like this:
["a", "b", "d", "g"]
To be able to get rid of this behavior, Dictionary (where you don't have to maintain previous indexes to have values) was introduced. So you can insert values as you like. It won't bother you for maintaining any index.

Related

Convert an object to array of size 1 in PostgreSQL jsonb and transform the json with nested arrays to rows

I have a two part question
We have a PostgreSQL table with a jsonb column. The values in jsonb are valid jsons, but they are such that for some rows a node would come in as an array whereas for others it will come as an object.
for example, the json we receive could either be like this ( node4 I just an object )
"node1": {
"node2": {
"node3": {
"node4": {
"attr1": "7d181b05-9c9b-4759-9368-aa7a38b0dc69",
"attr2": "S1",
"UserID": "WebServices",
"attr3": "S&P 500*",
"attr4": "EI",
"attr5": "0"
}
}
}
}
Or like this ( node4 is an array )
"node1": {
"node2": {
"node3": {
"node4": [
{
"attr1": "7d181b05-9c9b-4759-9368-aa7a38b0dc69",
"attr2": "S1",
"UserID": "WebServices",
"attr3": "S&P 500*",
"attr4": "EI",
"attr5": "0"
},
{
"attr1": "7d181b05-9c9b-4759-9368-aa7a38b0dc69",
"attr2": "S1",
"UserID": "WebServices",
"attr3": "S&P 500*",
"attr4": "EI",
"attr5": "0"
}
]
}
}
}
And I have to write a jsonpath query to extract, for example, attr1, for each PostgreSQL row containing this json. I would like to have just one jsonpath query that would always work irrespective of whether the node is object or array. So, I want to use a path like below, assuming, if it is an array, it will return the value for all indices in that array.
jsonb_path_query(payload, '$.node1.node2.node3.node4[*].attr1')#>> '{}' AS "ATTR1"
I would like to avoid checking whether the type in array or object and then run a separate query for each and do a union.
Is it possible?
A sub-question related to above - Since I needed the output as text without the quotes, and somewhere I saw to use #>> '{}' - so I tried that and it is working, but can someone explain, how that works?
The second part of the question is - the incoming json can have multiple sets of nested arrays and the json and the number of nodes is huge. So other part I would like to do is flatten the json into multiple rows. The examples I found were one has to identify each level and either use cross join or unnest. What I was hoping is there is a way to flatten a node that is an array, including all of the parent information, without knowing which, if any, if its parents are arrays or simple object. Is this possible as well?
Update
I tried to look at the documentation and tried to understand the #>> '{}' construct, and then I came to realise that '{}' is the right hand operand for the #>> operator which takes a path and in my case the path is the current attribute value hence {}. Looking at examples that had non-empty single attribute path helped me realise that.
Thank you
You can use a "recursive term" in the JSON path expression:
select t.some_column,
p.attr1
from the_table t
cross join jsonb_path_query(payload, 'strict $.**.attr1') as p(attr1)
Note that the strict modifier is required, otherwise, each value will be returned multiple times.
This will return one row for each key attr1 found in any level of the JSON structure.
For the given sample data, this would return:
attr1
--------------------------------------
"7d181b05-9c9b-4759-9368-aa7a38b0dc69"
"7d181b05-9c9b-4759-9368-aa7a38b0dc69"
"7d181b05-9c9b-4759-9368-aa7a38b0dc69"
"I would like to avoid checking whether the type in array or object and then run a separate query for each and do a union. Is it possible?"
Yes it is and your jsonpath query works fine in both cases either when node4 is a jsonb object or when it is a jsonb array because the jsonpath wildcard array accessor [*] also works with a jsonb object in the lax mode which is the default behavior (but not in the strict mode see the manual). See the test results in dbfiddle.
"I saw to use #>> '{}' - so I tried that and it is working, but can someone explain, how that works?"
The output of the jsonb_path_query function is of type jsonb, and when the result is a jsonb string, then it is automatically displayed with double quotes " in the query results. The operator #>> converts the output into the text type which is displayed without " in the query results and the associated text array '{}' just point at the root of the passed jsonb data.
" the incoming json can have multiple sets of nested arrays and the json and the number of nodes is huge. So other part I would like to do is flatten the json into multiple rows"
you can refer to the answer of a_horse_with_no_name using the recursive wildcard member accessor .**

Swift 5 Firebase queryOrderedBy String value

is it possible to query after the child value if its a string? In alphabetically order?
Doesnt matter whether it is descending or ascending.
e.g. under the key, each reference has the assigned name of the follower, and I want to order all the followers alphabetically.
Only manage to query it ordered by an integer unfortunately. (INCLUDING PAGINATION)
If this doesnt work, is there a way to query ordered by key? e.g. I have key 1 "-edasMmaed" and key 2 "-deLkdnw" etc and that if do paginate I start after the last value?
I haven't found anything useful unfortunately.
Kind regards
Edit: This is for the first part of the question
EDIT 2:
var query = Ref().databaseFollowingForUser(uid: userId, type: type).queryOrderedByKey()
if let latestUserFollowers = uid, latestUserFollowers != 0 {
query = query.queryEnding(atValue: latestUserFollowers).queryLimited(toLast: limit)
} else {
query = query.queryLimited(toLast: limit)
}
query.observeSingleEvent(of: .value, with: { (snapshot) in
With this code I receive the first 10 results (limit is defined as 10)
everbody from ID: 276 through ID: 18. (starting at holgerhagerson and ending at manni85)
Now I want to paginate and load more which I am not able yet.
The passed uid is the uid of the latest fetched user which is "18", manni85
BIG EDIT: I managed to order it by keys. Reading your answers regarding keys are always saved as strings, I realized my mistake and are now able to do it properly.
Big thank you!
Keys in the Firebase Realtime Database are stored (and sorted) as strings. Even if they look like numbers to you, Firebase will store (and sort) them as strings.
This means that the 2, 3, 4, etc in your screenshot are actually "2", "3", "4" etc. This affects how they are ordered, as strings are ordered lexicographically and in that order these keys will show up as "3", "30", "4", "44", "5", etc.
If you want to use keys that you can sort numerically, take these steps:
Prefix keys with a short non-numeric prefix, to prevent Firebase interpreting them as an array.
Use a fixed length for the numbers in all keys, prefixing the value with zeroes or spaces as needed.
When combined, your keys would show up as:
"key_003": ...,
"key_004": ...,
...
"key_008": ...,
"key_016": ...,
"key_018": ...,
"key_030": ...,
"key_044": ...
Which you can now reliably sort when you query /FOLLOW/Follower/2 by calling queryOrderedByKey().

plpgsql jsonb_set for JSON array of objects with nested arrays

Using PostgreSQL 14.0 PL/SQL (inside a do block). Attempting to:
query a certain key ('county') in a jsonb array of objects (which in turn has object + nested arrays) based on dynamic variable value (named cty.cty_name)
retrieve value and change it
update said jsonb to reflect the updated value in (2)
after executing (3) on multiple values, create new table with above jsonb as a row with one column
steps (1) and (2) execute properly. But, for the life of me, I can't figure (3) out.
jsonb object(res) -- may have 100s of array items at index root:
[ {"county": "x", "dpa": ["a", "b", "c"]},
{"county": "y", "dpa": ["d", "e", "f"]},
{"county": "z", "dpa": ["h", "i", "j"]},
...
]
code for (1) and (2) above:
execute format('select jsonb_path_query_array(''%s'', ''$[*]?(#.%s=="%s")'')',
res,'county',cty.cty_name) into s1;
execute format('select jsonb_array_elements(''%s'')->''%s''', s1,'dpa') into s2;
s2 := s2 || jsonb_build_array(r1.name);
where say:
cty.cty_name is y (which is created from a select in for loop)
r1.name is m
s2 holds the new value e.g. ["d", "e", "f", "m"]
Now, to execute (3) I need path to dpa for which key county matches value y in some index in res. Having tried (and failed miserably) at various permutations of jsonb_query_path with SQL/JSON Path, dollar-quoted strings, jsonb_path_to_array with double-quoted hell for format queries, other SO solutions which use idx or idx-1 (but I don't have JSON in table), I had to resort to soliciting the Borg collective's wisdom. Help please.
The problem you're running into with the current approach is twofold:
there's no way to "delete" the matching county object in vitro (via jsonb_set(), etc.)
there's no way to force uniqueness (to utilize the ON CONFLICT ... DO UPDATE mechanism) within the json document itself to accomplish the same
When we get to
Now, to execute (3) I need path to dpa for which key county matches value y in some index in res.
instead of updating the existing record in-place, why not just remove the matching record (with now-stale value for "dpa"), re-aggregate what remains (i.e. the non-matching objects), and then append the updated matching object to the aggregated jsonb array, similar to:
SELECT jsonb_agg(a) || jsonb_build_object('county', 'y', 'dpa', (jsonb_path_query_array(res, '$[*] ? (#.county=="y")')#>'{0,dpa}') || jsonb_build_array('m') )
FROM jsonb_array_elements(res) a
WHERE NOT a #> (jsonb_path_query_array(res, '$[*] ? (#.county=="y")')->0);
This gives a single jsonb value back per your specification in (4); you should be able to parameterize this into your EXECUTE invocation as necessary.
Worth noting on the output order, if you're looping over the initial "res" array, then the order of the objects within the array (with respect to the "county" values of the driving cursor) will be restored according to the order of the cursor you're iterating through for "county".
This is because a full cycle through said cursor will remove each of the old objects and replace them at the end of the resultant array, so defining an ORDER BY clause in this cursor will be important if this is relevant.

how to query to a collection with array in mongodb

I Have An Array With 4 Object Items I want query to my collection and return 4 items that have this uid's...
myArray = > [{uid : 'test'},{uid : 'test2'},{uid : 'test3'},{uid : 'test4'}]
ProductCollection.find({????},(err,result)=>{})
NOTE : I dont want use any loop
I dont want use any loop
I will assume that's related to query the DB several times, one for each uid value.
Anyway, you can go to the database once to filter elements that match an array of values, like your case, using MongoDB's $in operator.
But you would have to format the uid values to an array of the values themselves instead of the array of objets, this can be accomplished with a simple .map call (don't know if you will consider this a loop) to get the filter value in the correct format.
var uids = myArray.map((item) => item.uid })
// ['test', 'test2', 'test3', 'test4']
And after that you can query your DB with this uids values
ProductCollection.find({'uid': {'$in': uids} },(err,result)=>{})
(Assuming 'uid' it the property you have in your ProductCollection that you are trying to filter by)

Sorting an Array

I have an array containing all string values. Contents of the array are order_id, waypoint_x, waypoint_y. How do I sort it based on order_id and have the results like 1, 2, 3, 4.... (i.e. as if the field was an integer type and string)
When I use the waypoints as the array is at the moment the results come out 1, 10, 11, 12...
Regards,
Stephen
If you check the documentation of NSArray, you'll see there are different methods for sorting the array: sortedArrayUsingFunction, sortedArrayUsingSelector, sortedArrayUsingComparator, etc.
There's a nice example of how to use sortedArrayUsingFunction to sort with integer values in the answer to this question: Sorting an array of doubles or CLLocationDistance values on the iPhone