Full addresses from nested dictionaries - python-3.x

We have data like this
input = {
'a': 3,
'b': {'g': {'l': 12}},
'c': {
'q': 3,
'w': {'v': 3},
'r': 8,
'g': 4
},
'd': 4
}
It is not known in advance how many nesting levels there will be
We need to get the full address to the final value, all points of which are separated by a dot, or another special character
Like this:
a:3
b.g.l: 12
c.q: 3
c.w.v: 3
etc
I tried to solve this problem with a recursive function.
def recursive_parse(data: dict, cache: Optional[list]=None):
if cache is None:
cache = []
for k in data:
cache.append(k)
if not isinstance(data[k], dict):
print(f"{'.'.join(cache) } :{data[k]}")
cache.clear()
else:
recursive_parse(data[k], cache)
But I have problems with "remembering" the previous key of the nested dictionary.
a :3
b.g.l :12
c.q :3
w.v :3
r :8
g :4
d :4
What is the correct algorithm to solve this?

It's probably better to use an explicit stack for this, rather than the Python call stack. Recursion is slow in Python, due to high function call overhead, and the recursion limit is fairly conservative.
def dotted(data):
result = {}
stack = list(data.items())
while stack:
k0, v0 = stack.pop()
if isinstance(v0, dict):
for k1, v1 in v0.items():
item = ".".join([k0, k1]), v1
stack.append(item)
else:
result[k0] = v0
return result
Demo:
>>> data
{'a': 3,
'b': {'g': {'l': 12}},
'c': {'q': 3, 'w': {'v': 3}, 'r': 8, 'g': 4},
'd': 4}
>>> for k, v in reversed(dotted(data).items()):
... print(k, v)
...
a 3
b.g.l 12
c.q 3
c.w.v 3
c.r 8
c.g 4
d 4

Try:
dct = {
"a": 3,
"b": {"g": {"l": 12}},
"c": {"q": 3, "w": {"v": 3}, "r": 8, "g": 4},
"d": 4,
}
def parse(d, path=None):
if path is None:
path = []
if isinstance(d, dict):
for k, v in d.items():
yield from parse(v, path + [k])
else:
yield "{}: {}".format(".".join(path), d)
for p in parse(dct):
print(p)
Prints:
a: 3
b.g.l: 12
c.q: 3
c.w.v: 3
c.r: 8
c.g: 4
d: 4

Related

How to check the values in two dictionaries have the same type?

For example, I have two dictionaries having the same keys:
a = {"a": 1, "b": 2, "c":4.5, "d":[1,2], "e":"string", "f":{"f1":0.0, "f2":1.5}}
b = {"a": 10, "b": 20, "c":3.5, "d":[0,2,4], "e":"q", "f":{"f1":1.0, "f2":0.0}}
and I want to compare the types. My code is something like this:
if type(a["a"]) == type(b["a"]) and type(a["b"]) == type(b["b"]) and type(a["c"]) == type(b["c"]) and type(a["d"]) == type(b["d"]) and type(a["e"]) == type(b["e"]) and type(a["f"]) == type(b["f"]) and type(a["f"]["f1"]) == type(b["f"]["f1"]) and type(a["f"]["f2"]) == type(b["f"]["f2"]):
first_type = type(b["d"][0])
if all( (type(x) is first_type) for x in a["d"] )
#do something
pass
Is there a better way to do it?
You can make a list of the common keys between the dicts:
common_keys = a.keys() & b.keys()
and then iterate over them to check the types:
for k in common_keys:
if type(a[k]) == type(b[k]):
print("Yes, same type! " + k, a[k], b[k])
else:
print("Nope! " + k, a[k], b[k])
and if you wanted to go deeper, check if any of the items are dicts, rinse an repeat
for k in common_keys:
if type(a[k]) == type(b[k]):
print("Yes, same type! " + k, type(a[k]), type(b[k]))
if isinstance(a[k], dict):
ck = a[k].keys() & b[k].keys()
for key in ck:
if type(a[k][key]) == type(b[k][key]):
print("Yes, same type! " + key, type(a[k][key]), type(b[k][key]))
else:
print("Nope!")
else:
print("Nope! " + k, type(a[k]), type(b[k]))
You can use a for loop to iterate through the dicts:
same_types = True
for key in a.keys():
if type(a[key]) != type(b[key]):
same_types = False
break
# if the value is a dict, check nested value types
if type(a[key]) == dict:
for nest_key in a[key].keys():
if type(a[key][nest_key]) != type(b[key][nest_key]):
same_types = False
break
# if the value is a list, check all list elements
# I just simply concat two lists together, you can also refer to
# https://stackoverflow.com/q/35554208/19322223
elif type(a[key]) == list:
first_type = a[key][0]
for elem in a[key] + b[key]:
if type(elem) != first_type:
same_types = False
break
if not same_types:
break
if same_types:
# do something
With the following helper function:
def get_types(obj, items=None):
"""Function that recursively traverses 'obj' and returns
a list of all values and nested values types
"""
if not items:
items = []
if isinstance(obj, dict):
for value in obj.values():
if not isinstance(value, (dict, list, set, tuple)):
items.append(value)
else:
get_types(value, items)
elif isinstance(obj, (list, set, tuple)):
for value in obj:
get_types(value, items)
else:
items.append(obj)
return [type(x) for x in items]
You can compare two dictionaries' values types however deeply nested these are, like this:
if get_types(a) == get_types(b):
print("Each a and b values are of same types")
Since, in your example, a misses one value for d key ([1, 2]) compared to the other dict ([0, 2, 4]), nothing will be printed.
Let's take another example where both dictionaries have the same shape this time, but one value of different type (f2):
a = {"a": 1, "b": [[1, 2], [3, [4]]], "c": {"c1": 0.0, "c2": {"x": "9"}}}
b = {"d": 7, "e": [[2, 1], [5, [7]]], "f": {"f1": 8.9, "f2": {"y": 9}}}
if get_types(a) == get_types(b):
print("Each a and b values are of same types")
Then again, nothing will be printed.
But if you replace 9 by "9" in b["f2"]:
a = {"a": 1, "b": [[1, 2], [3, [4]]], "c": {"c1": 0.0, "c2": {"x": "9"}}}
b = {"d": 7, "e": [[2, 1], [5, [7]]], "f": {"f1": 8.9, "f2": {"y": "9"}}}
if get_types(a) == get_types(b):
print("Each a and b values are of same types")
# Output
# Each a and b values are of same types

Python3 for loop over a dict

I am trying to get all the values individually for each asset (BCHUSD and TRXUSD).
What I want to do is something like this:
BCHUSD a = 301.340000 b = 301.160000 c = 301.280000
TRXUSD a = 0.0609450 b = 0.0609440 c = 0.0609540
Could someone tell me how I can do it please?
Regards!
import requests
import json
while True:
req = requests.get('https://api.kraken.com/0/public/Ticker?pair=BCHUSD,TRXUSD,XRPUSD')
print(req)
<Response [200]>
print(type(req))
<class 'requests.models.Response'>
obj = req.json()
print(type(obj))
<class 'dict'>
for k, v in obj.items():
if type(v) is dict and k:
for nk, nv in v.items():
print(nk, nv)
BCHUSD {'a': ['298.240000', '11', '11.000'], 'b': ['298.040000', '3', '3.000'], 'c':
['299.000000', '0.89507885'], 'v': ['38.42175237', '5614.56089299'], 'p':
['300.890848', '277.650439'], 't': [24, 2314], 'l': ['299.000000', '260.000000'], 'h':
['302.390000', '309.900000'], 'o': '299.000000'}
TRXUSD {'a': ['0.0608250', '4881', '4881.000'], 'b': ['0.0607820', '40500',
'40500.000'], 'c': ['0.0608630', '81.94337742'], 'v': ['21067.61432979',
'9622286.56922629'], 'p': ['0.0610566', '0.0589675'], 't': [25, 1729], 'l':
['0.0608630', '0.0562060'], 'h': ['0.0612840', '0.0618410'], 'o': '0.0611130'}
XXRPZUSD {'a': ['0.69018000', '666', '666.000'], 'b': ['0.69000000', '42829',
'42829.000'], 'c': ['0.69022000', '358.00000000'], 'v': ['287549.02071579',
'27810492.67564827'], 'p': ['0.69737332', '0.65981291'], 't': [429, 10340], 'l':
['0.69000000', '0.62229000'], 'h': ['0.70386000', '0.72105000'], 'o': '0.69935000'}
I think the following could help you as a starting point:
response_json = {
"title": "name",
"abc": {'a': [1,2,3], "b": [2,3,4]},
"ohter_stuff": "xxx",
"xyz": {'a': [10, 20 ,30], "b": [20, 30, 40]}
}
# select relevant key value pairs
data = {
key: value for key, value in response_json.items()
if isinstance(value, dict)
}
# get the inner subdict index length
length = len(data['abc']['a'])
# get the inner subdict keys
items = list(data['abc'].keys())
# loop and print
for index in range(length):
for name, subdict in data.items():
# join the items at index pairs into a string
index_items = " ".join(
f"{item} = {subdict[item][index]}"
for item in items
)
print(name, index_items)
This is a pure standard library python solution. If you can install other libraries, I would recommend to have a look into pandas.

count the frequency of each character using the dictionary in python

My program that takes a string as an input from the user and counts the frequency of each character using the dictionary.
Input:
Python programming is fun
Expected output:
{'p': 2, 'y': 1, 't': 1, 'h': 1, 'o': 2, 'n': 3, 'r': 2, 'g': 2, 'a': 1, 'm': 2, 'i': 2, 's': 1, 'f': 1, 'u': 1}
My code:
string = input().lower()
dicx = {}
count = 0
for i in string:
dicx['i'] = ''
print(dicx)
Use collections.Counter
dicx = collections.Counter(string.lower())
You can iterate over string and update the dictionary accordingly and also there's no need of any count variable.
test_str = input().lower()
dicx = {}
for i in test_str:
if i in dicx:
dicx[i] += 1
else:
dicx[i] = 1
print(dicx)
Function takes input as string and counts the character and stores them in a dictionary
from typing import Dict
char_dict = {} #type: Dict[str, int]
def char_count(string: str) -> dict:
new_string = string.lower()
for c in new_string:
if c in char_dict:
char_dict[c] += 1
else:
char_dict[c] = 1
return char_dict
if __name__ == "__main__":
UserString = input("Enter Input String: ")
CharCount = char_count(UserString)
print("Characters Count: ", CharCount)
Example:
Enter Input String: Python programming is fun
Characters Count: {'p': 2, 'y': 1, 't': 1, 'h': 1, 'o': 2, 'n': 3, ' ': 3, 'r': 2, 'g': 2, 'a': 1, 'm': 2, 'i': 2, 's': 1, 'f': 1, 'u': 1}
Way 1: For
symbols = {}
for s in inp_str.lower():
if s in symbols:
symbols[s] += 1
else:
symbols.update({s: 1})
print(symbols)
Way 2: defaultdict
symbols = defaultdict(int)
for s in inp_str.lower():
symbols[s] += 1
print(symbols)
Way 3: Counter
symbols = Counter(inp_str.lower())
print(symbols)
def charCounter(string):
empty = {}
for i in string.lower():
if i in empty.keys():
empty[i] += 1
else:
empty[i] = 1
return empty
print(charCounter("Oh, it is python"))
d = {}
test_str = input().lower()
for x in test_str:
d[x] = d.get(x,0) + 1
print(d)
much more elegant like this

How can I improve this algorithm to count the frequency of characters in a string?

In order to sort in a descending manner, the frequency of char appearance in a string, I've developed the following algorithm.
First I pass the string to a dictionary using each char as a key along with its frequency of appearance as value. Afterwards I have converted the dictionary to a descending sorted multi-dimension list.
I'd like to know how to improve the algorithm, was it a good approach? Can it be done diferently? All proposals are welcome.
#Libraries
from operator import itemgetter
# START
# Function
# String to Dict. Value as freq.
# of appearance and char as key.
def frequencyChar(string):
#string = string.lower() # Optional
freq = 0
thisDict = {}
for char in string:
if char.isalpha(): # just chars
freq = string.count(char)
thisDict[char] = freq # {key:value}
return(thisDict)
str2Dict = frequencyChar("Would you like to travel with me?")
#print(str2Dict)
# Dictionary to list
list_key_value = [[k,v] for k, v in str2Dict.items()]
# Descending sorted list
list_key_value = sorted(list_key_value, key=itemgetter(1), reverse=True)
print("\n", list_key_value, "\n")
#END
You're doing way too much work. collections.Counter counts things for you automatically, and even sorts by frequency:
from collections import Counter
s = "Would you like to travel with me?"
freq = Counter(s)
# Counter({' ': 6, 'o': 3, 'l': 3, 'e': 3, 't': 3, 'u': 2, 'i': 2, 'W': 1, 'd': 1, 'y': 1, 'k': 1, 'r': 1, 'a': 1, 'v': 1, 'w': 1, 'h': 1, 'm': 1, '?': 1})
If you want to remove the spaces from the count:
del freq[' ']
# Counter({'o': 3, 'l': 3, 'e': 3, 't': 3, 'u': 2, 'i': 2, 'W': 1, 'd': 1, 'y': 1, 'k': 1, 'r': 1, 'a': 1, 'v': 1, 'w': 1, 'h': 1, 'm': 1, '?': 1})
Also just in general, your algorithm is doing too much work. string.count involves iterating over the whole string for each character you're trying to count. Instead, you can just iterate once over the whole string, and for every letter you just keep incrementing the key associated with that letter (initialize it to 1 if it's a letter you haven't seen before). That's essentially what Counter is doing for you.
Spelling it out:
count = {}
for letter in the_string:
if not letter.isalpha():
continue
if letter not in count:
count[letter] = 1
else:
count[letter] += 1
And then to sort it you don't need to convert to a list first, you can just do it directly:
ordered = sorted(count.items(), key=itemgetter(1), reverse=True)

counting letter frequency with a dict

I'm trying to find the frequency of letters without the Counter.And the code will output a dictionary form of result. And what I have done so far is to make the program count the word frequencies but not the letter/character frequencies. If anyone could point out my mistakes in this code that would be wonderful. Thank you.
It supposed to look like this:
{'a':2,'b':1,'c':1,'d':1,'z':1}
**but this is what I am actually getting:
{'abc':1,'az':1,'ed':1}
**my code is below
word_list=['abc','az','ed']
def count_letter_frequency(word_list):
letter_frequency={}
for word in word_list:
keys=letter_frequency.keys()
if word in keys:
letter_frequency[word]+=1
else:
letter_frequency[word]=1
return letter_frequency
Use collections.Counter
from collections import Counter
print Counter(''.join(word_list))
# Counter({'a': 2, 'c': 1, 'b': 1, 'e': 1, 'd': 1, 'z': 1})
Or count the elements yourself if you don't want to use Counter.
from collections import defaultdict
d = defaultdict(int)
for c in ''.join(word_list):
d[c] += 1
print d
# defaultdict(<type 'int'>, {'a': 2, 'c': 1, 'b': 1, 'e': 1, 'd': 1, 'z': 1})
This is the correct code:
word_list=['abc','az','ed']
def count_letter_frequency(word_list):
letter_frequency={}
for word in word_list:
for letter in word:
keys=letter_frequency.keys()
if letter in keys:
letter_frequency[letter]+=1
else:
letter_frequency[letter]=1
return letter_frequency
You were iterating over the list and the list contains words. So, you were making words as keys in your dictionary. So, you have to add another for loop to iterate over the letters in each word.
Would this be acceptable:
flat = ''.join(word_list)
{l: flat.count(l) for l in set(flat)}
#{'a': 2, 'b': 1, 'c': 1, 'd': 1, 'e': 1, 'z': 1}
If you would prefer this in for loop, here it goes:
flat = ''.join(word_list)
result = {}
for l in flat:
if l in result:
result[l] += 1
else:
result[l] = 1

Resources