Flatten array of array of dictionaries, taking only values - swift

I have this array structure:
[
[
keyA: value1,
keyB: value2
],
[
keyA: value3,
keyB: value4
],
[
keyA: value5,
keyB: value6
]
]
And what I want to achieve is flattening the array into a single dictionary like:
[value1: value2, value3: value4]
Probably to achieve this is using merge twice?
I have tried with:
arrayToFlatten.reduce([:]) { $0.merging($1) { (current, _) in current } }
but I did not get the expected result:
[
[keyA: value1],
[keyB: value2]
]
This structure is sourced from a plist file:

If I understand this question correctly, I think this is what you're looking for
let source = [
[
"code": "DZ",
"name": "ALGERIA",
],
[
"code": "AS",
"name": "AMERICAN SAMOA",
],
[
"code": "AO",
"name": "ANGOLA",
],
]
let result = Dictionary(uniqueKeysWithValues: source.lazy.map { dict in
return (key: dict["code"]!, value: dict["name"]!)
})
print(result) // => ["AO": "ANGOLA", "AS": "AMERICAN SAMOA", "DZ": "ALGERIA"]
Dictionary merging wasn't the right tool for the job. It would take the dict, merge it with the second, which has the same keys. According to the closure you gave it, when two keys clash, it should take the old value and not the one that's attempting to be merged in. So merging the second dict had no effect. Similarly, the third didn't have any effect, either. You should read the documentation.
I hope today you've learned the importance of a minimal, reproducible example and a clear question. If you had just said "here's my source data, here's the expected output," then a question like this could have been answered in seconds.

Related

Postgresql get keys from array of objects in JSONB field

Here' a dummy data for the jsonb column
[ { "name": [ "sun11", "sun12" ], "alignment": "center", "more": "fields" }, { "name": [ "sun12", "sun13" ], "alignment": "center" }, { "name": [ "sun14", "sun15" ] }]
I want to fetch all the name keys value from jsonb array of objects...expecting output -
[ [ "sun11", "sun12" ], [ "sun12", "sun13" ], [ "sun14", "sun15" ] ]
The problem is that I'm able to fetch the name key value by giving the index like 0, 1, etc
SELECT data->0->'name' FROM public."user";
[ "sun11", "sun12" ]
But I'm not able to get all the name keys values from same array of object.I Just want to get all the keys values from the array of json object. Any help will be helpful. Thanks
demo:db<>fiddle (Final query first, intermediate steps below)
WITH data AS (
SELECT '[ { "name": [ "sun11", "sun12" ], "alignment": "center", "more": "fields" }, { "name": [ "sun12", "sun13" ], "alignment": "center" }, { "name": [ "sun14", "sun15" ] }]'::jsonb AS jsondata
)
SELECT
jsonb_agg(elems.value -> 'name') -- 2
FROM
data,
jsonb_array_elements(jsondata) AS elems -- 1
jsonb_array_elements() expands every array element into one row
-> operator gives the array for attribute name; after that jsonb_agg() puts all extracted arrays into one again.
my example
SELECT DISTINCT sub.name FROM (
SELECT
jsonb_build_object('name', p.data->'name') AS name
FROM user AS u
WHERE u.data IS NOT NULL
) sub
WHERE sub.name != '{"name": null}';

Update a nested array mongoose

I've asked this before, but people just want to label this a duplicate instead of answering it.
Looking for a simple answer here. Can the below be done or not?
I have a huge schema that I inherited. It has an array of objects and I am targeting an array of objects nested within that. cssClass is what I'm trying to update. Here is the schema
board: {
"lists": [
{ "id": 12,
"cards": [
{ "id": 123,
"cssClass": "default"
},
{ "id": 124,
"cssClass": "default"
}
]
}
]
}

mongodb check regex on fields from one collection to all fields in other collection

After digging google and SO for a week I've ended up asking the question here. Suppose there are two collections,
UsersCollection:
[
{...
name:"James"
userregex: "a|regex|str|here"
},
{...
name:"James"
userregex: "another|regex|string|there"
},
...
]
PostCollection:
[
{...
title:"a string here ..."
},
{...
title: "another string here ..."
},
...
]
I need to get all users whose userregex will match any post.title(Need user_id, post_id groups or something similar).
What I've tried so far:
1. Get all users in collection, run regex on all products, works but too dirty! it'll have to execute a query for each user
2. Same as above, but using a foreach in Mongo query, it's the same as above but only Database layer instead of application layer
I searched alot for available methods such as aggregations, upwind etc with no luck.
So is it possible to do this in Mongo? Should i change my database type? if yes what type would be good? performance is my first priority. Thanks
It is not possible to reference the regex field stored in the document in the regex operator inside match expression.
So it can't be done in mongo side with current structure.
$lookup works well with equality condition. So one alternative ( similar to what Nic suggested ) would be update your post collection to include an extra field called keywords ( array of keyword values it can be searched on ) for each title.
db.users.aggregate([
{$lookup: {
from: "posts",
localField: "userregex",
foreignField: "keywords",
as: "posts"
}
}
])
The above query will do something like this (works from 3.4).
keywords: { $in: [ userregex.elem1, userregex.elem2, ... ] }.
From the docs
If the field holds an array, then the $in operator selects the
documents whose field holds an array that contains at least one
element that matches a value in the specified array (e.g. ,
, etc.)
It looks like earlier versions ( tested on 3.2 ) will only match if array have same order, values and length of arrays is same.
Sample Input:
Users
db.users.insertMany([
{
"name": "James",
"userregex": [
"another",
"here"
]
},
{
"name": "John",
"userregex": [
"another",
"string"
]
}
])
Posts
db.posts.insertMany([
{
"title": "a string here",
"keyword": [
"here"
]
},
{
"title": "another string here",
"keywords": [
"another",
"here"
]
},
{
"title": "one string here",
"keywords": [
"string"
]
}
])
Sample Output:
[
{
"name": "James",
"userregex": [
"another",
"here"
],
"posts": [
{
"title": "another string here",
"keywords": [
"another",
"here"
]
},
{
"title": "a string here",
"keywords": [
"here"
]
}
]
},
{
"name": "John",
"userregex": [
"another",
"string"
],
"posts": [
{
"title": "another string here",
"keywords": [
"another",
"here"
]
},
{
"title": "one string here",
"keywords": [
"string"
]
}
]
}
]
MongoDB is good for your use case but you need to use a approach different from current one. Since you are only concerned about any title matching any post, you can store the last results of such a match. Below is a example code
db.users.find({last_post_id: {$exists: 0}}).forEach(
function(row) {
var regex = new RegExp(row['userregex']);
var found = db.post_collection.findOne({title: regex});
if (found) {
post_id = found["post_id"];
db.users.updateOne({
user_id: row["user_id"]
}, {
$set :{ last_post_id: post_id}
});
}
}
)
What it does is that only filters users which don't have last_post_id set, searches post records for that and sets the last_post_id if a record is found. So after running this, you can return the results like
db.users.find({last_post_id: {$exists: 1}}, {user_id:1, last_post_id:1, _id:0})
The only thing you need to be concerned about is a edit/delete to an existing post. So after every edit/delete, you should just run below, so that all matches for that post id are run again.
post_id_changed = 1
db.users.updateMany({last_post_id: post_id_changed}, {$unset: {last_post_id: 1}})
This will make sure that next time you run the update these users are processed again. The approach does have one drawback that for every user without a matching title, the query for such users would run again and again. Though you can workaround that by using some timestamps or post count check
Also you should make to sure to put index on post_collection.title
I was thinking that if you pre-tokenized your post titles like this:
{
"_id": ...
"title": "Another string there",
"keywords": [
"another",
"string",
"there"
]
}
but unfortunately $lookup requires that foreignField is a single element, so my idea of something like this will not work :( But maybe it will give you another idea?
db.Post.aggregate([
{$lookup: {
from: "Users",
localField: "keywords",
foreignField: "keywords",
as: "users"
}
},
]))

MongoDB updating wrong subdocument in an array

my gamefamilies collection looks like this
{
"_id": ObjectId('54cc3ee7894ae60c1c9d6c74'),
"game_ref_id": "REF123",
..
"yearwise_details": [
{
"year": 1,
...
"other_details": [
{
"type": "cash",
"openingstock": 988
..
},
{
"type": "FLU",
"openingstock": 555
..
},
..other items
]
},
{
"year": 2,
...
"other_details": [
{
"type": "cash",
"openingstock": 3000,
....
},
...
{
"type": "ghee",
"openingstock": 3000,
...
},
..
]
}
]
}
My update query
db.gamefamilies.update({"game_ref_id": "REF123", "teamname": "manisha","yearwise_details.year": 2, "yearwise_details.other_details.type": "ghee"}, {"$set": {"yearwise_details.0.other_details.$.openingstock": 555} });
Document is getting picked up correctly. I expect to update year 2's item type="ghee" but instead year 1's 2nd item (type FLU) gets updated. What am I doing wrong ?
Any help would be greatly appreciated.
regards
Manisha
Unfortunately, there is not yet support for nested $ positional operator updates.
So you can hardcode the update with
db.gamefamilies.update({"game_ref_id": "REF123",
"teamname": "manisha",
"yearwise_details.year": 2,
"yearwise_details.other_details.type": "ghee"},
{"$set":
{"yearwise_details.1.other_details.$.openingstock": 555}});
But notice that the yearwise_details.1.other_details is hardcoding that you want the second value of the array (it is 0-indexed, so the 1 is referencing the second element). I am assuming you found the command you have in your question because it worked for the first element of the array. But it will only ever work on the first element and the command above will only ever work on the second element.

syntax issue ? passing objects as parameters in neo4j cypher statement

I am running a query like this using the REST API to the transaction endpoint:
{
"statements" : [{"statement":"MATCH (n)-[r]-(m) WHERE id(n) IN {diagramnodes} return [type(r),labels(m)] ",
"parameters" :{
"diagramnodes" : [28]
}}]
}
which returns the expected result:
{
"commit": "http://myserver:7474/db/data/transaction/542/commit",
"results": [
{
"columns": [
"[type(r),labels(m)]"
],
"data": [
{
"row": [
[
"CONTAINS",
[
"Sentence"
]
]
]
},
{
"row": [
[
"CONTAINS",
[
"Prologram",
"Diagram"
]
]
]
},
.......
]
}
],
"transaction": {
"expires": "Sun, 07 Sep 2014 17:50:11 +0000"
},
"errors": []
}
When adding another parameter and a filter to limit the types of rels that are returned:
{"statements": [{
"statement": "MATCH (n)-[r]-(m) WHERE id(n) IN {diagramnodes} AND [type(r),labels(m)] IN {includerels} return r ",
"parameters": {
"diagramnodes": [28],
"includerels": [
[
"CONTAINS",
[
"Prologram",
"Diagram"
]
],
[
"HAS_TARGET",
["Term"]
]
]
}
}]}
it does not return any results. Why?
I found a workaround, by concatenating the reltype and labels, and comparing it to a collection of primitive types. This is the cypher (added some CRLF to make it easier to read)
{
"statements" : [{"statement":"
MATCH (n)-[r]-(m)
WHERE id(n) IN {diagramnodes}
WITH type(r) as rtype, REDUCE(acc = '', p IN labels(m)| acc + ' '+ p) AS mlabels,m
WITH rtype+mlabels As rtypemlabels,m
WHERE rtypemlabels IN {includerels}
RETURN rtypemlabels,id(m) ",
"parameters" :{
"diagramnodes" : [28],
"includerels": ["HAS_TARGET Term","CONTAINS Sentence","CONTAINS Prologram Diagram"]
}}]
}
Note 1 : type(r)+REDUCE(acc = '', p IN labels(m)| acc + ' '+ p) does not work, you have to insert an additional WITH
Note 2 : comparing a collection of nested objects with an IN clause should be possible and remains on my wish list. ;)
IN operations very probably only work for collections of primitive values.
what you can try to do is to rewrite it into ALL(x in coll WHERE expr(x)) predicate.
for an input like:
[["CONTAINS",["Prologram","Diagram"]],
["HAS_TARGET",["Term"]]]
you can try:
ALL(entry in {includerels} WHERE type(r) = entry[0] AND ALL(l in labels(n) WHERE l in entry[1]))
You can use UNWIND on your parameters array instead of using IN. Depending on your data, you might have to use DISTINCT as well. But UNWIND works well for me.