Get specific value by key from nested dictionaries/lists - list

I am trying to return "blue" only (ignore other colors) from color_menu in this list/dict:
{'id': 10, 'color_menu': [{'color': 'blue', 'name': 'Sandra'}, {'color': 'red', 'name': 'Lisa'}]}
I am able to return all three colors but haven't been able to isolate and return just blue.

Just chain the indexes:
>>> x = {'id': 10, 'color_menu': [{'color': 'blue', 'name': 'Sandra'}, {'color': 'red', 'name': 'Lisa'}]}
>>> x["color_menu"][0]["color"]
'blue'
Note that this is kinda unreadable: only do it this way if you can't reshape the data structures in any way.
Explanation:
dictionaries are unordered and are indexed (usually) by key, though it's possible to index by value. in this case, the keys are strings and the values are more objects.
lists are indexed by their numeric index.
dictionaries inside lists inside dictionaries (known as "nested") are no different: the syntax is sensical.

Related

Python: insert new element into sorted list of dictionaries

I've got a list of dictionaries that is already sorted by a key id.
y = [{'id': 0, 'name': 'Frank'},
{'id': 5, 'name': 'Hank'},
{'id': 8, 'name': 'Fred'},
{'id': 30, 'name': 'Jill'}]
I want to insert a new element into the list.
y.append({'id': 6, 'name': 'Jenkins'})
How do I avoid sorting the list again as follows once the new element is added?
y = sorted(y, key=lambda x: x['id'])
The ideal outcome is:
y = [{'id': 0, 'name': 'Frank'},
{'id': 5, 'name': 'Hank'},
{'id': 6, 'name': 'Jenkins'},
{'id': 8, 'name': 'Fred'},
{'id': 30, 'name': 'Jill'}]
Edit:
Using bisect.insort(y, {'id': 6, 'name': 'Jenkins'}) will work only for the first key, if the dict is sorted by name, it will fail.
Since a insertion in a list is in O(n) anyway, any clever bisect algorithm is not that useful, so you can simply loop the list to find the position where it should be inserted, and then insert it. Something like:
new_value = {'id': 6, 'name': 'Jenkins'}
for index, value in enumerate(y):
# Assuming y is in increasing order.
if value['id'] > new_value['id']:
y.insert(index, new_value)
break
I recommend to change your dict to a class, overload the comparison operator, and then use bisect.insort(). I don't recommend to iterate over all items to find the insert location as proposed in the other reply. From theory, the complexity is still O(n), but, but searching the item takes much longer than inserting a new element. I guess inserting will just do a few memcopies, while for the search you have to compare every single element.
From my example, iterating to find the location, then insert takes about 2 minutes to complete. The same data with bisect.insert() takes about 10 seconds.

conditional background color in python - new to python

I'm trying to write a work script into excel from Python to excel. I have had to format the size and type of the columns already, but I need one of the columns to have conditional colors.
ex./
column A has percentages.
For all cells < 0%, we need the cells to be red
For all cells = 0%, we need the cells to be yellow
For all cells > 0%, we need the cells to be green
And then, the first row of each column needs to be blue.
I've found scripts similar, but they aren't as conditional.
I've tried a few variations of
# Light red fill.
format1 = name.add_format({'bg_color': '#FFC7CE'})
# Light yellow fill.
format2 = name.add_format({'bg_color': '#FFEB9C'})
# Green fill.
format3 = name.add_format({'bg_color': '#C6EFCE'})
try:
sheet1.conditional_format('A:A', {'type': 'cell',
'criteria': '<',
'value': 0,
'format': format1})
sheet1.conditional_format('A:A', {'type': 'cell',
'criteria': '==',
'value': 0,
'format': format2})
sheet1.conditional_format('A:A', {'type': 'cell',
'criteria': '>',
'value': 0,
'format': format3})
except AttributeError:
sheet1.conditional_format = None
except TypeError:
type = None
but it doesn't change the colors
and I've tried a few variations of
while 'BU:BU' != " " :
if 'BU:BU' < '0%':
sheet1.style = redFill
elif 'BU:BU' == '0%':
sheet1.style = yellowFill
elif 'BU:BU' > '0%':
sheet1.style = '#C6EFCE'
else:
pass
continue
but that keeps getting stuck in an infinite loop.
Thanks
I figured out the problem. In order for this to work, the three formats cannot be in the same cell. Each needs to be in its own with the exceptions.
Also, boundaries need to be put on how many cells it runs on. If it is something that will be run multiple times with a changing number of rows, you can shoot high.
One cell:
# Light red fill.
format1 = name.add_format({'bg_color': '#FFC7CE'})
try:
sheet1.conditional_format('A1:A900', {'type': 'cell',
'criteria': '<',
'value': 0,
'format': format1})
except AttributeError:
sheet1.conditional_format = None
except TypeError:
type = None
Another cell:
# Light yellow fill.
format2 = name.add_format({'bg_color': '#FFEB9C'})
...

Reference a table column by its column header in Python

Is there a Pythonic way to refer to columns of 2D lists by name?
I import a lot of tables from the web so I made a general purpose function that creates 2 dimensional lists out of various HTML tables. So far so good. But the next step is often to parse the table row by row.
# Sample table.
# In real life I would do something like: table = HTML_table('url', 'table id')
table =
[
['Column A', 'Column B', 'Column C'],
['One', 'Two', 3],
['Four', 'Five', 6]
]
# Current code:
iA = table[0].index('Column A')
iB = tabel[0].index('Column B')
for row in table[1:]:
process_row(row[iA], row[iC])
# Desired code:
for row in table[1:]:
process_row(row['Column A'], row['Column C'])
I think you'll really like the pandas module! http://pandas.pydata.org/
Put your list into a DataFrame
This could also be done directly from html, csv, etc.
df = pd.DataFrame(table[1:], columns=table[0]).astype(str)
Access columns
df['Column A']
Access first row by index
df.iloc[0]
Process row by row
df.apply(lambda x: '_'.join(x), axis=0)
for index,row in df.iterrows():
process_row(row['Column A'], row['Column C'])
Process a column
df['Column C'].astype(int).sum()
Wouldn't a ordereddict of keys being columns names and values a list of rows be a better approach for your problem? I would go with something like:
table = {
'Column A': [1, 4],
'Column B': [2, 5],
'Column C': [3, 6]
}
# And you would parse column by column...
for col, rows in table.iteritems():
#do something
My QueryList is simple to use.
ql.filter(portfolio='123')
ql.group_by(['portfolio', 'ticker'])
class QueryList(list):
"""filter and/or group_by a list of objects."""
def group_by(self, attrs) -> dict:
"""Like a database group_by function.
args:
attrs: str or list.
Returns:
{value_of_the_group: list_of_matching_objects, ...}
When attrs is a list, each key is a tuple.
Ex:
{'AMZN': QueryList(),
'MSFT': QueryList(),
...
}
-- or --
{('Momentum', 'FB'): QueryList(),
...,
}
"""
result = defaultdict(QueryList)
if isinstance(attrs, str):
for item in self:
result[getattr(item, attrs)].append(item)
else:
for item in self:
result[tuple(getattr(item, x) for x in attrs)].append(item)
return result
def filter(self, **kwargs):
"""Returns the subset of IndexedList that has matching attributes.
args:
kwargs: Attribute name/value pairs.
Example:
foo.filter(portfolio='123', account='ABC').
"""
ordered_kwargs = OrderedDict(kwargs)
match = tuple(ordered_kwargs.values())
def is_match(item):
if tuple(getattr(item, y) for y in ordered_kwargs.keys()) == match:
return True
else:
return False
result = IndexedList([x for x in self if is_match(x)])
return result
def scalar(self, default=None, attr=None):
"""Returns the first item in this QueryList.
args:
default: The value to return if there is less than one item,
or if the attr is not found.
attr: Returns getattr(item, attr) if not None.
"""
item, = self[0:1] or [default]
if attr is None:
result = item
else:
result = getattr(item, attr, default)
return result
I tried pandas. I wanted to like it, I really did. But ultimately it is too complicated for my needs.
For example:
df[df['portfolio'] == '123'] & df['ticker'] == 'MSFT']]
is not as simple as
ql.filter(portfolio='123', ticker='MSFT')
Furthermore, creating a QueryList is simpler than creating a df.
That's because you tend to use custom classes with a QueryList. The data conversion code would naturally be placed into the custom class which keeps that separate from the rest of the logic. But data conversion for a df would normally be done inline with the rest of the code.

Displaying the same order of keys which I have in my list of dictionary keys in my output

I am using python 2.7.X version and I need to display my result of list of dictionary in key order same as in my output.
CREATE THE LIST OF DICTIONARY :
for acc_data in acc_pool.browse(cr,uid,acc_ids_in):
for line in acc_data.invoice_line:
c+=1
lst_data2.append({
'SupplierName':acc_data.partner_id.name or '',
'SupplierBRN':acc_data.partner_id.com_reg_no1 or '',
'InvoiceDate':acc_data.date_invoice or '',
'InvoiceNumber':acc_data.number or '',
'ImportDeclarationNo':'',
'LineNumber':c,
'ProductDescription':line.product_id.name or '',
'PurchaseValueMYR':line.price_unit or 0.00,
'GSTValueMYR':'',
'TaxCode':line.invoice_line_tax_id.name or '',
'FCYCode':'',
'PurchaseFCY':'',
'GSTFCY':'',
})
RESULT :
> lst_data2 [{'ProductDescription': u'Ink Cartridge', 'SupplierBRN': '', 'ImportDeclarationNo': '', 'GSTValueMYR': '', 'SupplierName': u'Vicking Direct', 'GSTFCY': '', 'TaxCode': u'Purchase Tax 15.00%', 'InvoiceDate': '2015-03-24', 'FCYCode': '', 'PurchaseFCY': '', 'PurchaseValueMYR': 58.0, 'LineNumber': 1, 'InvoiceNumber': u'EXJ/2015/002'}, {'ProductDescription': u'Toner Cartridge', 'SupplierBRN': '', 'ImportDeclarationNo': '', 'GSTValueMYR': '', 'SupplierName': u'Vicking Direct', 'GSTFCY': '', 'TaxCode': u'OTAX X', 'InvoiceDate': '2015-03-24', 'FCYCode': '', 'PurchaseFCY': '', 'PurchaseValueMYR': 65.0, 'LineNumber': 2, 'InvoiceNumber': u'EXJ/2015/002'}]
Here you can easily see my order of keys is different than the order of keys in my result.
My question is that I need to displaying the same order of keys which I have in my list of dictionary keys in my output.
How can I set the same order of list of keys to be in my result ?
dict does not remember order. ordereddict does; use that.
Unfortunately, you can't use the dict literal, because as soon as you do, you lose ordering. You can use this syntax:
from collections import OrderedDict
o = OrderedDict([("a", 1), ("b", 2)])
# `a` is first; `b` is second.
I have Resolved my posted Question.
Just We need to add the python Library our File and use it as follow.
from collections import OrderedDict
for acc_data in acc_pool.browse(cr,uid,acc_ids_in):
c=0
for line in acc_data.invoice_line:
if line.invoice_line_tax_id:
c+=1
lst_data2.append(OrderedDict([
('SupplierName',acc_data.partner_id.name or ''),
('SupplierBRN',acc_data.partner_id.com_reg_no or ''),
('InoiveDate',acc_data.date_invoice or ''),
('InvoiceNumber',acc_data.number or ''),
('ImportDeclarationNo',''),
('LineNumber',c),
('ProductDescription',line.product_id.name or ''),
('PurchaseValueMYR',line.price_unit or 0.00),
('GSTValueMYR',''),
('TaxCode',line.invoice_line_tax_id.name or ''),
('FCYCode',''),
('PurchaseFCY',''),
('GSTFCY',''),
]))
I hope this should helpful for some one. :)

Sorted dicts lists manual changing

In my views I have a dict list such as
data = [{'name': 'Abe', 'seq': [4, 3]}, {'name': 'All names', 'seq': [9, 8]}, {'name': 'Mike', 'seq': [11, 2]}]
In my template I sorted them with:
{{ data|dictsort:"name" }}
I'd like to have {'name': 'All names', 'seq': [9, 8]} in the top of the list and keep the other sorted data items. How can I do this?
Do the sorting in the view, using a custom key function.
data.sort(key=lambda d: '0' if d['name'] == 'All names' else d['name'])