Dictionary as value in dictionary - python

There is a dictionary with dictionaries as the value inside like so:
{'a': {'b': 'c', 'd': 'e', 'f': 'g'}, 'h': {'i': 'j', 'k': 'l', 'm': 'n'}}
How is it possible to access, let's say; the 'f' key (producing 'g') for 'a'?

Just chain key look-ups:
outer_dictionary['a']['f']
Here dictionary['a'] returns the value for the key 'a', which is itself a dictionary.
You could also store the intermediary result:
nested_dictionary = outer_dictionary['a']
nested_dictionary['f']
This does the exact same thing, but also leaves another reference to the nested dictionary available as nested_dictionary.
Quick demo:
>>> nested_dictionary = {'a': {'b': 'c', 'd': 'e', 'f': 'g'}, 'h': {'i': 'j', 'k': 'l', 'm': 'n'}}
>>> nested_dictionary['a']
{'b': 'c', 'd': 'e', 'f': 'g'}
>>> nested_dictionary['a']['f']
'g'

Related

Comparing 2 lists of dictionaries and adding a key,value when a common key is identified

I have two lists of dictionaries. The first list will contain significantly more dictionaries than the second list. There could be up to 200-300 dictionaries in list1 and no more than 10-15 dictionaries in list2.
For example, any dictionary in list1 that has the same 'g': h key/value as that of list2 needs to add key/value 'j': k to list 1.
list1 = [{'a': b, 'c': d, 'e': f, 'g': h},
{'a': b, 'c': d, 'e': f, 'g': h},
{'a': b, 'c': d, 'e': f, 'g': h},
{'a': b, 'c': d, 'e': f, 'g': h}
]
list2 = [{'g': h, 'j': k}]
I'm struggling on finding any previous examples of this type and cannot figure out a function of my own.
A trivial implementation could be:
for d1 in list1:
for d2 in list2:
if any(pair in d1.items() for pair in d2.items()):
d1.update(d2)
The value of list1 after this transformation:
[{'a': 'b', 'c': 'd', 'e': 'f', 'g': 'h', 'j': 'k'},
{'a': 'b', 'c': 'd', 'e': 'f', 'g': 'h', 'j': 'k'},
{'a': 'b', 'c': 'd', 'e': 'f', 'g': 'h', 'j': 'k'},
{'a': 'b', 'c': 'd', 'e': 'f', 'g': 'h', 'j': 'k'}]

Is there are more efficient method for creating a dictionary of letters mapped to letters shifted by a set integer?

I have written a function which takes an integer and returns a dictionary where the letters of the alphabet are mapped to an alphabet shifted by the integer. It has the requirement that lowercase and uppercase letters must correpsond, that is if a -> b then A->B.
I'm fairly new to python and have little experience with creating lists so I'm just wondering if there is an alternative more efficient or even just more elegant way to complete the same outcome.
import string
dictionary = []
for letter in string.ascii_lowercase + string.ascii_uppercase:
dictionary.append(letter)
def build_shift(shift):
shifted_dictionary = []
for i in range(0,26):
shifted_dictionary.append(dictionary[(i + shift) % 26])
for i in range(26, 53):
shifted_dictionary.append(dictionary[(( i + shift) % 26) + 26])
return (shifted_dictionary)
mapped_dictionary = dict( zip(dictionary, build_shift(2)))
print(mapped_dictionary)
You can use a dict comprehension that iterates characters over each of the lowercase and uppercase strings to output the character mapped to the one at the current index plus the offset and reduced with a modulo of 26. Below is an example using 2 as the offset:
{
c: s[(i + 2) % 26]
for s in (string.ascii_lowercase, string.ascii_uppercase)
for i, c in enumerate(s)
}
You can slice the string and get the desired result in less number of operations.
import string
shift = 2
changed_lc = f'{string.ascii_lowercase[shift:]}{string.ascii_lowercase[:shift]}'
changed_up = f'{string.ascii_uppercase[shift:]}{string.ascii_uppercase[:shift]}'
changed = f'{changed_lc}{changed_up}'
mapped_dictionary = dict(zip(string.ascii_letters, changed))
print(mapped_dictionary)
output:
{'a': 'c', 'b': 'd', 'c': 'e', 'd': 'f', 'e': 'g', 'f': 'h', 'g': 'i', 'h': 'j', 'i': 'k', 'j': 'l', 'k': 'm', 'l': 'n', 'm': 'o', 'n': 'p', 'o': 'q', 'p': 'r', 'q': 's', 'r': 't', 's': 'u', 't': 'v', 'u': 'w', 'v': 'x', 'w': 'y', 'x': 'z', 'y': 'a', 'z': 'b', 'A': 'C', 'B': 'D', 'C': 'E', 'D': 'F', 'E': 'G', 'F': 'H', 'G': 'I', 'H': 'J', 'I': 'K', 'J': 'L', 'K': 'M', 'L': 'N', 'M': 'O', 'N': 'P', 'O': 'Q', 'P': 'R', 'Q': 'S', 'R': 'T', 'S': 'U', 'T': 'V', 'U': 'W', 'V': 'X', 'W': 'Y', 'X': 'Z', 'Y': 'A', 'Z': 'B'}

Dictionary rearrangement and sorting

Required of counting the number of different values appear in the dict books, and in accordance with the number of occurrences of value reverse output.
books = {
123457889: 'A',
252435234: 'A',
434234341: 'B',
534524365: 'C',
354546589: 'D',
146546547: 'D',
353464543: 'F',
586746547: 'E',
511546547: 'F',
546546647: 'F',
541146127: 'F',
246546127: 'A',
434545127: 'B',
533346127: 'E',
544446127: 'F',
546446127: 'G',
155654627: 'G',
546567627: 'G',
145452437: 'H',
}
Output like this:
'F': 5,
'A': 3,
'G': 3,
'B': 2,
'D': 2,
'E': 2,
'C': 1,
'H': 1
I tried it:
import pprint
# to get the values from books
clist = [v for v in books.values()]
# values in books as keys in count,
count = {}
for c in clist:
count.setdefault(c, 0)
count[c] += 1
pprint.pprint(count)
But dict couldn't sorting.
Your code works fine. You can do this much easier using Counter from the collections module to do this for you. Simply pass books.values() in to Counter:
from collections import Counter
counts = Counter(books.values())
print(counts)
Output:
Counter({'F': 5, 'A': 3, 'G': 3, 'E': 2, 'D': 2, 'B': 2, 'H': 1, 'C': 1})
To provide the layout of the output you are expecting in order of value, you can perform a simple iteration using the most_common method and print each line:
for char, value in counts.most_common():
print("'{}': {}".format(char, value))
Output:
'F': 5
'G': 3
'A': 3
'E': 2
'D': 2
'B': 2
'C': 1
'H': 1

Iterating over list

I am currently doing a small pet project and i have come this far, currently my code accepts the string, changes the string into the respective cipher and displays it but its displaying the whole iterated string. What i am doing wrong ? i want only the translated string.
Code
def encrypt_letter(letter):
cipher = {'a': 'n', 'b': 'o', 'c': 'p', 'd': 'q',
'e': 'r', 'f': 's', 'g': 't', 'h': 'u',
'i': 'v', 'j': 'w', 'k': 'x', 'l': 'y',
'm': 'z', 'n': 'a', 'o': 'b', 'p': 'c',
'q': 'd', 'r': 'e', 's': 'f', 't': 'g',
'u': 'h', 'v': 'i', 'w': 'j', 'x': 'k',
'y': 'l', 'z': 'm'}
lowercase_letter = letter.lower()
return cipher[lowercase_letter]
def encrypt(string):
result = []
letters = list(string)
for letter in letters:
encrypted_letter = encrypt_letter(letter)
result.append(encrypted_letter)
print "".join(result)
e = encrypt("hello")
print e
Output
u
ur
ury
uryy
uryyb
None
Expected output
'uryyb'
One minor change will do! What you want is to return the string.
def encrypt(string):
result = []
letters = list(string)
for letter in letters:
encrypted_letter = encrypt_letter(letter)
result.append(encrypted_letter)
return "".join(result) # change to return
e = encrypt("hello")
print e # will give you expected output
In fact, you can write shorter code with this:
def encrypt(string):
cipher = {'a': 'n', 'b': 'o', 'c': 'p', 'd': 'q',
'e': 'r', 'f': 's', 'g': 't', 'h': 'u',
'i': 'v', 'j': 'w', 'k': 'x', 'l': 'y',
'm': 'z', 'n': 'a', 'o': 'b', 'p': 'c',
'q': 'd', 'r': 'e', 's': 'f', 't': 'g',
'u': 'h', 'v': 'i', 'w': 'j', 'x': 'k',
'y': 'l', 'z': 'm'}
return ''.join(cipher[s] for s in string.lower())
You are printing out the list with each iteration of the for-loop. You need to do two things to fix the problem:
Dedent print "".join(result) one level.
In that line, change print to return.
Here is the full code:
def encrypt_letter(letter):
cipher = {'a': 'n', 'b': 'o', 'c': 'p', 'd': 'q',
'e': 'r', 'f': 's', 'g': 't', 'h': 'u',
'i': 'v', 'j': 'w', 'k': 'x', 'l': 'y',
'm': 'z', 'n': 'a', 'o': 'b', 'p': 'c',
'q': 'd', 'r': 'e', 's': 'f', 't': 'g',
'u': 'h', 'v': 'i', 'w': 'j', 'x': 'k',
'y': 'l', 'z': 'm'}
lowercase_letter = letter.lower()
return cipher[lowercase_letter]
def encrypt(string):
result = []
letters = list(string)
for letter in letters:
encrypted_letter = encrypt_letter(letter)
result.append(encrypted_letter)
######################
return "".join(result)
######################
e = encrypt("hello")
print e
Output:
uryyb
Your print is inside the for loop; dedent it one level, i.e.
result.append(encrypted_letter)
print "".join(result)
This will prevent the repeated printing out through the loop. Also, nothing gets returned from your function, add:
return result
at the end (either additional to or replacing the print) to send your output to e.
The problem is that
print "".join(result)
falls in the for loop.
def encrypt(string):
return ''.join([encrypt_letter(letter) for letter in string])
output:
uryyb
I'd return a string from encrypt, not print one.
def encrypt(string):
result = ""
for letter in list(string):
result += encrypt_letter(letter)
return result
print(encrypt("hello"))

Python Caesar cypher

I am trying to make a dict that can apply a Caesar Cypher to a letter.
I need it to be in one dict for upper and lower but cannot figure put how to get both into one dict.
import string
def Coder(shift):
alpha = string.ascii_lowercase
ALPHA = string.ascii_uppercase
if shift in range(0,26):
return dict(zip(ALPHA, ALPHA[shift:] + ALPHA[0:shift])), dict(zip(alpha, alpha[shift:] + alpha[0:shift]))
Something like this:
import string
def Coder(shift):
alpha = string.ascii_lowercase
ALPHA = string.ascii_uppercase
if 0 <= shift < 26:
unshifted_letters = ALPHA + alpha
shifted_letters = ALPHA[shift:] + ALPHA[:shift] + alpha[shift:] + alpha[:shift]
return dict(zip(unshifted_letters, shifted_letters))
But as others have said, better solutions are encode('rot13') and string.maketrans. In particular, this: "rot_13 rot13 Unicode string Returns the Caesar-cypher encryption of the operand".
If you're new to programming, avoid trying to "do everything on one line".
string.index( substring ) finds the position of substring in string.
a % b takes the remainder of a divided by b.
string.upper() returns an uppercased version of string.
Knowing this, you should be able to understand every line in this program:
import string
translation = {}
shift = 5
alphabet = string.ascii_lowercase
for letter in alphabet:
position = alphabet.index( letter )
new_position = (position + shift) % len( alphabet )
translation[ letter ] = alphabet[ new_position ]
translation[ letter.upper() ] = alphabet[ new_position ].upper()
You can use dict.update():
First create a dictionary of uppercase letters and then update that dict with a dictionary of lowercase letters:
In [8]: from string import *
In [9]: al=ascii_lowercase
In [10]: au=ascii_uppercase
In [11]: for shift in range(2):
dic1=dict(zip(au, au[shift:] + au[0:shift]))
dic1.update(dict(zip(al, al[shift:] + al[0:shift])))
print dic1
....:
{'A': 'A', 'C': 'C', 'B': 'B', 'E': 'E', 'D': 'D', 'G': 'G', 'F': 'F', 'I': 'I', 'H': 'H', 'K': 'K', 'J': 'J', 'M': 'M', 'L': 'L', 'O': 'O', 'N': 'N', 'Q': 'Q', 'P': 'P', 'S': 'S', 'R': 'R', 'U': 'U', 'T': 'T', 'W': 'W', 'V': 'V', 'Y': 'Y', 'X': 'X', 'Z': 'Z', 'a': 'a', 'c': 'c', 'b': 'b', 'e': 'e', 'd': 'd', 'g': 'g', 'f': 'f', 'i': 'i', 'h': 'h', 'k': 'k', 'j': 'j', 'm': 'm', 'l': 'l', 'o': 'o', 'n': 'n', 'q': 'q', 'p': 'p', 's': 's', 'r': 'r', 'u': 'u', 't': 't', 'w': 'w', 'v': 'v', 'y': 'y', 'x': 'x', 'z': 'z'}
{'A': 'B', 'C': 'D', 'B': 'C', 'E': 'F', 'D': 'E', 'G': 'H', 'F': 'G', 'I': 'J', 'H': 'I', 'K': 'L', 'J': 'K', 'M': 'N', 'L': 'M', 'O': 'P', 'N': 'O', 'Q': 'R', 'P': 'Q', 'S': 'T', 'R': 'S', 'U': 'V', 'T': 'U', 'W': 'X', 'V': 'W', 'Y': 'Z', 'X': 'Y', 'Z': 'A', 'a': 'b', 'c': 'd', 'b': 'c', 'e': 'f', 'd': 'e', 'g': 'h', 'f': 'g', 'i': 'j', 'h': 'i', 'k': 'l', 'j': 'k', 'm': 'n', 'l': 'm', 'o': 'p', 'n': 'o', 'q': 'r', 'p': 'q', 's': 't', 'r': 's', 'u': 'v', 't': 'u', 'w': 'x', 'v': 'w', 'y': 'z', 'x': 'y', 'z': 'a'}
or you can also use str.translate() with string.maketrans:
for shift in xrange(4):
t=maketrans(au+al,au[shift:]+au[:shift]+al[shift:]+al[:shift])
print "abcxyzABCXYZ".translate(t)
....:
abcxyzABCXYZ
bcdyzaBCDYZA
cdezabCDEZAB
defabcDEFABC
S.translate(table [,deletechars]) -> string
Return a copy of the string S, where all characters occurring in the
optional argument deletechars are removed, and the remaining
characters have been mapped through the given translation table, which
must be a string of length 256 or None. If the table argument is None,
no translation is applied and the operation simply removes the
characters in deletechars.

Categories

Resources