List comprehension with string split - python

The following code takes two inputs, item_name and sale_type. It will look through item_dict to find if any keys that contain part of the item_name or fully matches it and output the values.
I am trying to convert the following for loop into a list comprehension. I am fairly comfortable with the basic list comprehensions but in this case I require the text to be split and obtain the relevant results. I am not sure if what I am asking for is possible.
item_name = "GalaxyDevices"
sale_type = "buy"
item_dict = {"buy_Galaxy": [11111, 2232], "sell_Galaxy": [2111]}
results = []
for key, value in item_dict.items():
key = key.split("_")
if key[0] != sale_type:
continue
if key[1] in item_name:
results.extend(value)
print(results)
input / output:
item_name = "GalaxyDevices"
sale_type = "buy"
>>> [11111, 2232]
My failed attempt:
results = [value.split("_") for key, value in item_dict.items()]
Many thanks!

I think this should work:
results = [
value
for key, values in item_dict.items()
if (key.split('_')[0] == sale_type
and key.split('_')[1] in item_name)
for value in values
]

Use nested list comprehension and set to determine if any has intersection.
This however requires 3 rules:
Name has some kind of separator - CamelCase separation requires re.
Part of each name should not match any in dict. i.e. Hello_world matching Hello_buy and world_buy at same time for best result.
For one-liner:
name = input("name >> ")
type_ = input("transaction type >> ")
results = [item_dict[key] for key in [key_ for key_ in item_dict.keys() if type_ in key_] if set(name.split()) & set(key.split("_"))]
Explanation
results = [item_dict[key] for key in
[key_ for key_ in item_dict.keys() if type_ in key_]
if set(name.split()) & set(key.split("_"))]
Above equals to:
intersecting_words = lambda name_, keys_: set(name) & set(keys_)
key_matching_part = [key for key in item_dict.keys() if intersecting_words(name, key.split("_"))]
results = [item_dict[key] for key in key_matching_part]
Full test:
item_dict = {"Guido_buy": 100, "Guido_sell": -100,
"Ramalho_buy": 200, "Ramalho_sell": -200}
name = "Guido van Rossum"
type_ = "buy"
results = [item_dict[key] for key in [key_ for key_ in item_dict.keys() if type_ in key_] if set(name.split()) & set(key.split("_"))]
print(results)
name = "Luciano Ramalho"
type_ = "sell"
results = [item_dict[key] for key in [key_ for key_ in item_dict.keys() if type_ in key_] if set(name.split()) & set(key.split("_"))]
print(results)
Output:
[100]
[-200]

Related

Nested and escaped JSON payload to flattened dictionary - python

I'm looking for any suggestions to resolve an issue I'm facing. It might seem as a simple problem, but after a few days trying to find an answer - I think it is not anymore.
I'm receiving data (StringType) in a following JSON-like format, and there is a requirement to turn it into flat key-value pair dictionary. Here is a payload sample:
s = """{"status": "active", "name": "{\"first\": \"John\", \"last\": \"Smith\"}", "street_address": "100 \"Y\" Street"}"""
and the desired output should look like this:
{'status': 'active', 'name_first': 'John', 'name_last': 'Smith', 'street_address': '100 "Y" Street'}
The issue is I can't find a way to turn original string (s) into a dictionary. If I can achieve that the flattening part is working perfectly fine.
import json
import collections
import ast
#############################################################
# Flatten complex structure into a flat dictionary
#############################################################
def flatten_dictionary(dictionary, parent_key=False, separator='_', value_to_str=True):
"""
Turn a nested complex json into a flattened dictionary
:param dictionary: The dictionary to flatten
:param parent_key: The string to prepend to dictionary's keys
:param separator: The string used to separate flattened keys
:param value_to_str: Force all returned values to string type
:return: A flattened dictionary
"""
items = []
for key, value in dictionary.items():
new_key = str(parent_key) + separator + key if parent_key else key
try:
value = json.loads(value)
except BaseException:
value = value
if isinstance(value, collections.MutableMapping):
if not value.items():
items.append((new_key,None))
else:
items.extend(flatten_dictionary(value, new_key, separator).items())
elif isinstance(value, list):
if len(value):
for k, v in enumerate(value):
items.extend(flatten_dictionary({str(k): (str(v) if value_to_str else v)}, new_key).items())
else:
items.append((new_key,None))
else:
items.append((new_key, (str(value) if value_to_str else value)))
return dict(items)
# Data sample; sting and dictionary
s = """{"status": "active", "name": "{\"first\": \"John\", \"last\": \"Smith\"}", "street_address": "100 \"Y\" Street"}"""
d = {"status": "active", "name": "{\"first\": \"John\", \"last\": \"Smith\"}", "street_address": "100 \"Y\" Street"}
# Works for dictionary type
print(flatten_dictionary(d))
# Doesn't work for string type, for any of the below methods
e = eval(s)
# a = ast.literal_eval(s)
# j = json.loads(s)
Try:
import json
import re
def jsonify(s):
s = s.replace('"{','{').replace('}"','}')
s = re.sub(r'street_address":\s+"(.+)"(.+)"(.+)"', r'street_address": "\1\2\3"',s)
return json.loads(s)
If you must keep the quotes around Y, try:
def jsonify(s):
s = s.replace('"{','{').replace('}"','}')
search = re.search(r'street_address":\s+"(.+)"(.+)"(.+)"',s)
if search:
s = re.sub(r'street_address":\s+"(.+)"(.+)"(.+)"', r'street_address": "\1\2\3"',s)
dict_version = json.loads(s)
dict_version['street_address'] = dict_version['street_address'].replace(search.group(2),'"'+search.group(2)+'"')
return dict_version
A more generalized attempt:
def jsonify(s):
pattern = r'(?<=[,}])\s*"(.[^\{\}:,]+?)":\s+"([^\{\}:,]+?)"([^\{\}:,]+?)"([^\{\}:,]+?)"([,\}])'
s = s.replace('"{','{').replace('}"','}')
search = re.search(pattern,s)
matches = []
if search:
matches = re.findall(pattern,s)
s = re.sub(pattern, r'"\1": "\2\3\4"\5',s)
dict_version = json.loads(s)
for match in matches:
dict_version[match[0]] = dict_version[match[0]].replace(match[2],'"'+match[2]+'"')
return dict_version

zip Cuts off After First Letter

I'm trying to run the following code:
def by_primary_key(table, key, fields) -> object:
key_columns = get_key_columns(table, key )
print("key columns in get by primary key " , key_columns)
print("key, " , key )
zip_it = list(zip(key_columns, key))
print("zip_it", zip_it )
dictt = dict(zip_it)
print("dict", dictt)
The output I want for zip_it is: [('playerID', 'willite01')]
but the output the program produces is:
key columns in get by primary key ['playerID']
key, willite01
zip_it [('playerID', 'w')]
dict {'playerID': 'w'}
Where am I going wrong?
The following worked
key_columns = get_key_columns(table, key )
lst = []
lst.append(key)
tmp = dict(zip(key_columns, lst))
result = find_by_template1(table, tmp, fields)
return result

python list of dictionaries only updating 1 attribute and skipping others

I have a list of lists containing company objects:
companies_list = [companies1, companies2]
I have the following function:
def get_fund_amount_by_year(companies_list):
companies_length = len(companies_list)
for idx, companies in enumerate(companies_list):
companies1 = companies.values_list('id', flat=True)
funding_rounds = FundingRound.objects.filter(company_id__in=companies1).order_by('announced_on')
amount_per_year_list = []
for fr in funding_rounds:
fr_year = fr.announced_on.year
fr_amount = fr.raised_amount_usd
if not any(d['year'] == fr_year for d in amount_per_year_list):
year_amount = {}
year_amount['year'] = fr_year
for companies_idx in range(companies_length):
year_amount['amount'+str(companies_idx)] = 0
if companies_idx == idx:
year_amount['amount'+str(companies_idx)] = fr_amount
amount_per_year_list.append(year_amount)
else:
for year_amount in amount_per_year_list:
if year_amount['year'] == fr_year:
year_amount['amount'+str(idx)] += fr_amount
return amount_per_year_list
The problem is the resulting list of dictionaries has only one amount attribute updated.
As you can see "amount0" contains all "0" amounts:
[{'amount1': 12100000L, 'amount0': 0, 'year': 1999}, {'amount1':
8900000L, 'amount0': 0, 'year': 2000}]
What am I doing wrong?
I put list of dictionaries being built in the loop and so when it iterated it overwrote the last input. I changed it to look like:
def get_fund_amount_by_year(companies_list):
companies_length = len(companies_list)
**amount_per_year_list = []**
for idx, companies in enumerate(companies_list):
companies1 = companies.values_list('id', flat=True)
funding_rounds = FundingRound.objects.filter(company_id__in=companies1).order_by('announced_on')

How can I sort a list of dicts arbitrarily by a particular key?

How can I sort my list of dicts by their name values according to an arbitrary ordering? I want dicts with a name of 720p to come first, then dicts with a name of 1080p, and finally dicts with a name of 360p.
hosters = []
for entry in json.loads(aResult[1][0]):
if 'file' not in entry or 'label' not in entry: continue
sLabel = sName + ' - ' + entry['label'].encode('utf-8')
hoster = dict()
hoster['link'] = entry['file']
hoster['name'] = sLabel
hoster['resolveable'] = True
hosters.append(hoster)
You're going to need to use a custom sorting function. Something like this should work:
def sort_by_resolution(hoster):
desired_order = ['720p', '1080p', '360p']
if hoster['name'] in desired_order:
return desired_order.index(hoster['name'])
else:
return len(desired_order)
sorted(foo, key=sort_by_resolution)
# [{'name': '720p'}, {'name': '1080p'}, {'name': '360p'}]

create a dict by spliting a string in an ordered dict

I have an ordered dict that represent field definition ie Name, type, width, precision
it looks like this:
OrderedDict([(u'CODE_MUN', 'str:8'), (u'CODE_DR_AN', 'str:8'),
(u'AREA', 'float:31.2'), (u'PERIMETER', 'float:31.4')])
I would like to create a dict for each item that would be like this:
{'name' : 'CODE_MUN', 'type': 'str', 'width': 8, 'precision':0} for fields without precision
and
{'name' : 'AREA', 'type': 'float', 'width': 31, 'precision':2 } for fiels with precision
for keys, values in fieldsDict.iteritems():
dict = {}
dict['name'] = keys
props = re.split(':.', values)
dict['type'] = props[0]
dict['width'] = props[1]
dict['precision'] = props[2]
of course I have index error when there is no precision defined. What would be the best way to achieve that?
Use a try-except block.
for keys, values in fieldsDict.iteritems():
dict = {}
dict['name'] = keys
props = re.split(':.', values)
dict['type'] = props[0]
dict['width'] = props[1]
try:
dict['precision'] = props[2]
except IndexError:
dict['precision'] = 0
You could also test for length using an if-else block. The methods are pretty close and I doubt this is a situation where it really matters, but for more on asking forgiveness vs permission you can see this question.
You have to check precision is there or not.
from collections import OrderedDict
import re
fieldsDict = OrderedDict([(u'CODE_MUN', 'str:8'), (u'CODE_DR_AN', 'str:8'),
(u'AREA', 'float:31.2'), (u'PERIMETER', 'float:31.4')])
for keys, values in fieldsDict.iteritems():
dict = {}
dict['name'] = keys
props = re.split(':.', values)
dict['type'] = props[0]
dict['width'] = props[1]
if len(props) == 3:
dict['precision'] = props[2]
else:
dict['precision'] = 0
print dict
This might be help

Categories

Resources