Iterate over N nested list and dictionaries - python-3.x

I have the following structure of JSON/Dict.
[
{
"childrens": [
{
"childrens": [
{
"name": "somenam1"
}
],
"name": "B999"
}
],
"name": "11111"
},
{
"childrens": [
{
"childrens": [
{
"name": "somename2"
},
{
"name": "somename3"
}
],
"name": "B5555"
},
{
"childrens": [
{
"name": "somename4"
}
],
"name": "B2222"
}
],
"name": "2222"
}
]
I want to iterate over all dictionaries and list inside root list and create single string for each dictionary inside root list.
Output will look like this (two lines):
1111|B999|somename1
2222|B5555|somename2|somename3|B2222|somename4
Also this is just an example i can have N nested childrens.

Looks like a good candidate for recursion:
def flatten(child):
if not child:
return child
return [child['name']] + [name for c in child.get('childrens', []) for name in flatten(c)]
In []:
for child in data:
print('|'.join(flatten(child)))
Out[]:
11111|B999|somenam1
2222|B5555|somename2|somename3|B2222|somename4
Sure you can just pass add a level arg and return that:
def flatten(child, level=0):
if not child:
return child
return [level] + [l for c in child.get('childrens', []) for l in flatten(c, level+1)]
In []:
for child in data:
print('|'.join(str(level) for level in flatten(child)))
Out[]:
0|1|2
0|1|2|2|1|2

Here's a solution by recursion
data_json = '[{"childrens":[{"childrens":[{"name":"somenam1"}],"name":"B999"}],"name":"11111"},{"childrens":[{"childrens":[{"name":"somename2"},{"name":"somename3"}],"name":"B5555"},{"childrens":[{"name":"somename4"}],"name":"B2222"}],"name":"2222"}]'
data = json.loads(data_json)
def get_names(data_dict):
if ("childrens" in data_dict):
ret_dict = "|".join(map(get_names, data_dict["childrens"]))
return data_dict["name"] + "|" + ret_dict
else:
return data_dict["name"]
def get_all_name(data):
for i in data:
print(get_names(i))
get_all_name(data)

Related

Python - How to get all keys where values are zero from list of dictionaries?

I have list of dictionaries that looks like this:
planets = [
{ "name": "Mercury", "moonCount": 0 },
{ "name": "Venus", "moonCount": 0 },
{ "name": "Earth", "moonCount": 1 },
{
"name": "Mars",
"moonCount": 2
},
{
"name": "Jupiter",
"moonCount": 67
},
{
"name": "Saturn",
"moonCount": 62
},
{
"name": "Uranus",
"moonCount": 27
},
{
"name": "Neptune",
"moonCount": 13
},
{
"name": "Pluto",
"moonCount": 4
}
]
I am trying to get the list of all planets which do not have moons.
I can do this very simply if turn this into panda dataframe like this:
import pandas as pd
df = pd.json_normalize(planets)
no_moon_planets = df.loc[df['moonCount']==0, 'name']
no_moon_planets = no_moon_planets.tolist()
no_moon_planets
But I need to do this without creating panda dataframe. So basically looking for solution where I can extract the list of the planet directly from list of dictionaries where the number of the moons iz zero.
Any help is appreciated.
Do the following :
Planets_without_moon = [i['name'] for i in planets if i['moonCount']==0]
You can simply do it with list comprehension:
zero_moon = [dic for dic in planets if dic["moonCount"]==0]
if you just want the name of the planet:
zero_moon = [dic["name"] for dic in planets if dic["moonCount"]==0]

Arango DB Filter query for print array of value

Given the following document structure:
{
"name": [
{
"use": "official",
"family": "Chalmers",
"given": [
"Peter",
"James"
]
},
{
"use": "usual",
"given": [
"Jim"
]
},
{
"use": "maiden",
"family": "Windsor",
"given": [
"Peter",
"James"
]
}
]
}
Query:
FOR client IN Patient FILTER client.name[*].use=='official' RETURN client.name[*].given
I have telecom and name array.
I want to query to compare if name[*].use=='official' then print corresponding give array.
Expected result:
"given": [
"Peter",
"James"
]
client.name[*].use is an array, so you need to use an array operator. It can be either of the following:
'string' in doc.attribute
doc.attribute ANY == 'string'
doc.attribute ANY IN ['string']
To return just the given names from the 'official' array, you can use a subquery:
RETURN { given:
FIRST(FOR name IN client.name FILTER name.use == 'official' LIMIT 1 RETURN name.given)
}
Alternatively, you can use an inline expression:
FOR client IN Patient
FILTER 'official' IN client.name[*].use
RETURN { given:
FIRST(client.name[* FILTER CURRENT.use == 'official' LIMIT 1 RETURN CURRENT.given])
}
Result:
[
{
"given": [
"Peter",
"James"
]
}
]
In your original post, the example document and query didn't match, but assuming the following structure:
{
"telecom": [
{
"use": "official",
"value": "+1 (03) 5555 6473 82"
},
{
"use": "mobile",
"value": "+1 (252) 5555 910 920 3"
}
],
"name": [
{
"use": "official",
"family": "Chalmers",
"given": [
"Peter",
"James"
]
},
{
"use": "usual",
"given": [
"Jim"
]
},
{
"use": "maiden",
"family": "Windsor",
"given": [
"Peter",
"James"
]
}
]
}
… here is a possible query:
FOR client IN Patient
FILTER LENGTH(client.telecom[* FILTER
CONTAINS(CURRENT.value, "(03) 5555 6473") AND
CURRENT.use == 'official']
)
RETURN {
given: client.name[* FILTER CURRENT.use == 'official' RETURN CURRENT.given]
}
Note that client.telecom[*].value LIKE "..." causes the array of phone numbers to be cast to a string "[\"+1 (03) 5555 6473 82\",\"+1 (252) 5555 910 920 3\"]" against which the LIKE operation is run - this kind of works, but it's not ideal.
CONTAINS() is also faster than LIKE with % wildcards on both sides.
It would be possible that there are multiple 'official' elements, which might require an extra level of array nesting. Above query produces:
[
{
"given": [
[
"Peter",
"James"
]
]
}
]
If you know that there is only one element or restrict it to one element explicitly then you can get rid of one of the wrapping square brackets with FIRST() or FLATTEN().

Adding Custom Page fields wagtail api returning empty array for related field

Following the doc at Adding custom page fields, im trying to add Todo's to a Task object/page model for them to be queryable via the api (same thing for todos, make parent Task queryable), however, the related field always returns an empty array (I would have expected the related (parent or child) Pages to be in the response:
class Task(Page):
CHOICES = [(i, i) for i in range(11)]
sub_order = models.IntegerField(default=1, choices=CHOICES)
content_panels = Page.content_panels + [
...
]
api_fields = [
APIField('title'),
APIField('todos'),
]
class Todo(Page):
CHOICES = [(i, i) for i in range(1, 11)]
sub_order = models.IntegerField(default=1, choices=CHOICES)
parent_page_types = ['app.Task']
page = models.ForeignKey('app.Task', on_delete=models.SET_NULL, null=True, blank=True, related_name='todos')
content_panels = Page.content_panels + [
...
]
api_fields = [
APIField('parent_page_types'),
APIField('sub_order'),
]
endpoint: .../api/v2/pages/?format=json&type=app.Task&fields=*
Response:
{
"meta": {
"total_count": 1
},
"items": [
{
"id": 18,
"meta": {
"type": "app.Task",
"detail_url": ".../api/v2/pages/18/",
"html_url": "theURL",
"slug": "aasdasdsad",
"show_in_menus": false,
"seo_title": "",
"search_description": "",
"first_published_at": "2019-02-05T14:13:09.596817Z"
},
"title": "aasdasdsad",
"parent_page_types": [
"app.ParentPageType"
],
"sub_order": 1,
"todos": [] <-------- EMPTY ?
}
]
}
UPDATE:
the sqlite db doesn't seem to be creating/adding the todos or tasks in the table when I create the records =/ . The field is showing up as null, still troubleshooting

How to find List of subMap with nested subMap

I have a List of Map with nested Map as well as below :-
def list = [
[
"description": "The issue is open and ready for the assignee to start work on it.",
"id": "1",
"name": "Open",
"statusCategory": [
"colorName": "blue-gray",
"id": 2,
"key": "new",
"name": "To Do",
]
],
[
"description": "This issue is being actively worked on at the moment by the assignee.",
"id": "3",
"name": "In Progress",
"statusCategory": [
"colorName": "yellow",
"id": 4,
"key": "indeterminate",
"name": "In Progress",
]
]
]
I have a task to get List of subMap with nested subMap. I'm doing some thing like as below :-
def getSubMap = { lst ->
lst.findResults { it.subMap(["id", "name", "statusCategory"])}
}
println getSubMap(list)
But its give me output as below :-
[
[
"id":1,
"name":"Open",
"statusCategory":[
"colorName":"blue-gray",
"id":2,
"key":"new",
"name":"To Do"
]
],
[
"id":"3",
"name":"In Progress",
"statusCategory":[
"colorName":"yellow",
"id":"4",
"key":"indeterminate",
"name":"In Progress"
]
]
]
As you can see I'm unable to get subMap of statusCategory key Map. Actually I want to get further subMap for nested Maps something like as below :-
[
[
"id":1,
"name":"Open",
"statusCategory":[
"id":"2",
"name":"To Do"
]
],
[
"id":"3",
"name":"In Progress",
"statusCategory":[
"id":"4",
"name":"In Progress"
]
]
]
To achieve this I'm trying as below :-
def getSubMap = { lst ->
lst.findResults { it.subMap(["id", "name", "statusCategory":["id","name"]])}
}
def modifiedList = getSubMap(list)
But it throws me Excpetion. And If I'm doing as below :-
def getSubMap = { lst ->
lst.findResults { it.subMap(["id", "name", "statusCategory"]).statusCategory.subMap(["id","name"])}
}
println getSubMap(list)
It gives only nested subMap as :-
[["id":"2", "name":"To Do"], ["id":"4", "name":"In Progress"]]
could anyone suggest me how to recurselvely find List of subMap with nested subMap if exist?
If your Map nesting is arbitrary, then you might want to consider something like this:
def nestedSubMap
nestedSubMap = { Map map, List keys ->
map.subMap(keys) + map.findAll { k, v -> v instanceof Map }.collectEntries { k, v -> [(k):nestedSubMap(v, keys)] }
}
Given your input and this closure, the following script:
def result = list.collect { nestedSubMap(it, ["id", "name"]) }
println '['
result.each { print it; println ',' }
println ']'
Produces this output:
[
[id:1, name:Open, statusCategory:[id:2, name:To Do]],
[id:3, name:In Progress, statusCategory:[id:4, name:In Progress]],
]
Given the original list, consider this:
def resultList = list.collect {
def fields = ["id", "name"]
def m = it.subMap(fields)
m["statusCategory"] = it["statusCategory"].subMap(fields)
return m
}
which supports these assertions:
assert 1 == resultList[0]["id"] as int
assert "Open" == resultList[0]["name"]
assert 2 == resultList[0]["statusCategory"]["id"] as int
assert "To Do" == resultList[0]["statusCategory"]["name"]
assert 3 == resultList[1]["id"] as int
assert "In Progress" == resultList[1]["name"]
assert 4 == resultList[1]["statusCategory"]["id"] as int
assert "In Progress" == resultList[1]["statusCategory"]["name"]

Performing a query on the lowest level of a tree-structured Dojo store

Let's say we have a nested data structure like so:
[
{
"name": "fruits",
"items": [
{ "name": "apple" ...}
{ "name": "lemon" ...}
{ "name": "peach" ...}
]
}
{
"name": "veggies",
"items": [
{ "name": "carrot" ...}
{ "name": "cabbage" ...}
]
}
{
"name": "meat",
"items": [
{ "name": "steak" ...}
{ "name": "pork" ...}
]
}
]
The above data is placed in a dojo/store/Memory. I want to perform a query for items that contain the letter "c", but only on the lower level (don't want to query the categories).
With a generic dojo/store/Memory, it's query function only applies a filter on the top level, so the code
store.query(function(item) {
return item.name.indexOf("c") != -1;
});
will only perform the query on the category names (fruits, veggies, etc) instead of the actual items.
Is there a straight-forward way to perform this query on the child nodes, and if there's a match, return all children as well as the parent? For instance, the "c" query would return the "fruits" node with it's "peach" child only, "veggies" would remain intact, and "meat" would be left out of the query results entirely.
You can of course define your own checking method in the store's query method. I don't check if this code runs perfectly, but I guess you could pretty much get what it's meant to do.
store.query(function(item) {
var found = {
name: "",
items: []
};
var children = item.items;
d_array.forEach(children, function(child) {
if (child.name.indexOf("c") != -1) {
found.name = item.name;
found.items.push(child);
}
});
return found;
});
Hope this helps.

Resources