Python nested dictionaries into reStructuredText bullet list - python

I have this dictionary:
d = {'B': {'ticket': ['two', 'three'], 'note': ['nothing to report']}, 'A': {'ticket': ['one'], 'note': ['my note']}, 'C': {'ticket': ['four'], 'note': ['none']}}
and I'm trying to convert it into a .rst document as a bullets list, like:
* A
* ticket:
* one
* note
* my note
* B
* ticket:
* two
* three
* note:
* nothing to report
* C
* ticket:
* four
* note:
* none
I read this approach but I cannot translate it into a bullet list
Thanks to all

For something like your specific example, what about this:
>>> for key, value in d.items():
... print('* {}'.format(key))
... for k, v in value.items():
... print(' * {}:'.format(k))
... for i in v:
... print(' * {}'.format(i))
...
* B
* note:
* nothing to report
* ticket:
* two
* three
* A
* note:
* my note
* ticket:
* one
* C
* note:
* none
* ticket:
* four

A more of less generic solution to your problem would be a recursive function:
def bullet_list(elements, level=0, indent_size=4):
try:
items = elements.items()
except AttributeError:
for bullet_point in elements:
yield '{}* {}'.format(' ' * (indent_size * level), bullet_point)
else:
for bullet_point, sub_points in items:
yield '{}* {}'.format(' ' * (indent_size * level), bullet_point)
yield from bullet_list(sub_points, level=level + 1, indent_size=indent_size)
for line in bullet_list(d):
print(line)
outputs:
* A
* note
* my note
* ticket
* one
* C
* note
* none
* ticket
* four
* B
* note
* nothing to report
* ticket
* two
* three
​Note however that the ordered is not guaranteed in dictionaries until very recent versions of python.

Ugly and dirty
def bullet(d, depth=1):
for k,v in d.items():
print(''.join([depth * ' ', '* ', k]))
if isinstance(v, dict):
bullet(v, depth+1)
else:
for e in v:
print(''.join([depth * ' ', ' * ', e]))

I would break the task down into three steps:-
1 - Sort the dictionary - as this is impossible it would be best to create a list of keys, sort this list then iterate through them
2 - Check if ticket exists and the for item in ticket to print them
3 - Check if note exists then print each item for note.

Related

how to make my horizontal histogram to vertical

this is a part of my program which generates * patterns from 4 user inputs
total= progresscount + trailercount + retrivercount + excludecount
print("....................................................")
print("Histogram")
print("Progress",progresscount,":" , "*" * progresscount)
print("Trailer",trailercount,":" , "*" * trailercount)
print("Retriver",retrivercount,":" , "*" * retrivercount)
print("Excluded",excludecount ,":" , "*" * excludecount )
print("....................................................")
if I run my whole program and enter 2 3 1 and 2 for each input requests, output looks like this
....................................................
Histogram
Progress 2 : **
Trailer 3 : ***
Retriver 1 : *
Excluded 2 : **
....................................................
and I want to make a vertical version like this
....................................................
Progress Trailing Retriever Excluded
* * * *
* * *
*
....................................................
help me to figure this out?
You can create a custom function like below:
def display_vhist(data):
cell_len = max([len(k) for k in data]) + 2
max_val = max(data.values())
# display header
print(*[h.center(cell_len) for h in data.keys()], sep='')
# display histogram
for i in range(1, max_val+1):
for v in data.values():
c = '*' if i <= v else ' '
print(c.center(cell_len), end='')
print()
Usage:
data = {'Progress': 2, 'Trailer': 3, 'Retriver': 1, 'Excluded': 1}
display_vhist(data)
# Output
Progress Trailer Retriver Excluded
* * * *
* *
*

Simple python histogram

result1 = ["Progress","Progress(MT)","ModuleRT","Exclude"]
result2 = [3,4,3,5]
def histogram (list1,list2):
for i in range (len(list1)):
print(list1[i])
for j in range (list2[i]):
print("","*")
histogram(result1,result2)
I'm trying to get the output like this, but I can't seem to get that.
Progress Progress(MT) ModuleRT Excluded
* * * *
*
Using center(), you can build the columns with the width corresponding to their respective title size. The histogram itself will need as many lines as the maximum value in result2. Each column should only print a star if the line index is less than the corresponding value.
result1 = ["Progress","Progress(MT)","ModuleRT","Exclude"]
result2 = [3,4,3,5]
print(*result1)
for i in range(max(result2)):
print(*( " *"[i<r].center(len(t)) for t,r in zip(result1,result2)))
Progress Progress(MT) ModuleRT Exclude
* * * *
* * * *
* * * *
* *
*
The histogram would look better if the columns were above the titles. You can do this by simply reversing the order of the line index:
for i in reversed(range(max(result2))):
print(*( " *"[i<r].center(len(t)) for t,r in zip(result1,result2)))
print(*result1)
*
* *
* * * *
* * * *
* * * *
Progress Progress(MT) ModuleRT Exclude
Converted to a function:
def histogram(titles,values):
print(*titles)
for i in range(max(values)):
print(*( " *"[i<v].center(len(t)) for t,v in zip(titles,values)))
Hello Alex and welcome on the Stackoverflow
I assume that, you want to get something like this below:
Progress * * *
Progress(MT) * * * *
ModuleRT * * *
Exclude * * * * *
Then, you have to modify your code, so that the print method does not add a new line after every * printed. To do so, you use end argument and set it to an empty character, like this: print("some string", end='')
So your new code would be:
result1 = ["Progress","Progress(MT)","ModuleRT","Exclude"]
result2 = [3,4,3,5]
def histogram (list1,list2):
for i in range (len(list1)):
print(list1[i], end='') # here is the end added
for j in range (list2[i]):
print("","*", end='') # here is the end added
histogram(result1,result2)
Nevertheless, it won't end-up in something like this:
Progress * * *Progress(MT) * * * *ModuleRT * * *Exclude * * * * *
The thing is, that there's no new line character after first outer for loop iteration. So you print a new line with an empty print at the end of the outer loop like this print("").
So finally your code would look like this:
result1 = ["Progress","Progress(MT)","ModuleRT","Exclude"]
result2 = [3,4,3,5]
def histogram (list1,list2):
for i in range (len(list1)):
print(list1[i], end='')
for j in range (list2[i]):
print("","*", end='')
print("") # here is the print
histogram(result1,result2)

Shortening a for loop which pads first and last value of several lists

I have 3 lists (EIRP,data3,data15) and I'm looking for a way to shorten this code:
ws=3
for i in range(ws):
EIRP.insert(0, EIRP[0])
EIRP.append(EIRP[-1])
data3.insert(0,data3[0])
data3.append(data3[-1])
data15.insert(0,data15[0])
data15.append(data15[-1])
EIRP[0:0] = EIRP[:1] * 3
EIRP.extend(EIRP[-1:] * 3)
data3[0:0] = data3[:1] * 3
data3.extend(data3[-1:] * 3)
data15[0:0] = data15[:1] * 3
data15.extend(data15[-1:] * 3)
even shorter
EIRP = EIRP[:1] * 3 + EIRP[:] + EIRP[-1:] * 3
data3 = data3[:1] * 3 + data3[:] + data3[-1:] * 3
data15 = data15[:1] * 3 + data15[:] + data15[-1:] * 3
li = [EIRP,data3,data15]
ws = 3
for i in range(ws):
for j in li:
j.insert(0, j[0])
j.append(j[-1])
I hope that this is what you want

building a tower of * in python

i'm working on this challenge, wherein based on a number given it will show the asterisk (*) increment per odd number. Basically the result should be if I run the function below and set the n_floor to 5 it should show the result below. My code somehow iterates per number of floor and increments the * per odd number but the result fails but this is because the spacing of the asterisk between those quotes are wrong. Any idea/tip how to fix this?
a. Correct Result
[' * ', ' *** ', ' ***** ', ' ******* ', '*********']
b. REsult from my script below:
['*', '***', '*****', '*******', '*********']
def tower_builder(n_floor):
a = '*'
b = 1
c= 0
result = []
num=range(1, n_floor+1)
# * to increment by odd number
for x in num:
c = a
result.append(c)
a += str('**')
return result
Here's a better way which calculates the width you need:
def tower_builder(n_floor):
result = []
width = (n_floor * 2) - 1
for x in range(1, 2 * n_floor, 2):
stars = x * '*'
line = stars.center(width)
result.append(line)
return result
assert tower_builder(1) == ['*']
assert tower_builder(2) == [' * ', '***']
assert tower_builder(3) == [' * ', ' *** ', '*****']
assert tower_builder(4) == [' * ', ' *** ', ' ***** ', '*******']
assert tower_builder(5) == [' * ', ' *** ', ' ***** ', ' ******* ', '*********']
Here is a one-liner function:
def tower_builder(n):
return [('*' * i).center(n * 2 - 1) for i in range(1, 2 * n + 1, 2)]
Because each row of the tower is formed of an odd number of *, we need to loop oddly, Thus we set the loop max to the 2 * n with step of 2 to ensure that we are looping through odds.
Then we use center function to give the tower the final Pyramidal shape.
Like the comments say, you will want to use str.center.
For a concrete example, it would also point out that every "floor" has an odd number of characters, so you can actually simplify your function a bit.
def tower_builder(n_floor):
window = '☐'
total_width = 20 # this may get bigger if you have a very tall building
floor_strings = []
for floor_idx in range(n_floor):
# because widths are always odd
width = 2 * floor_idx + 1
# construct this floor
floor_string = (window*width).center(total_width)
# add it to the list
floor_strings.append(floor_string)
# join them all together with newlines
return '\n'.join(floor_strings)
print(tower_builder(5))
☐
☐☐☐
☐☐☐☐☐
☐☐☐☐☐☐☐
☐☐☐☐☐☐☐☐☐
As a side note, you can actually calculate what total_width must be by starting with the widest floor (or calculating it, which isn't terribly hard) and using that as total_width.

Non-sequential substitution in SymPy

I'm trying to use [SymPy][1] to substitute multiple terms in an expression at the same time. I tried the [subs function][2] with a dictionary as parameter, but found out that it substitutes sequentially.
In : a.subs({a:b, b:c})
Out: c
The problem is the first substitution resulted in a term that can be substituted by the second substitution, but it should not (for my cause).
Any idea on how to perform the substitutions simultaneously, without them interfering with each other?
Edit:
This is a real example
In [1]: I_x, I_y, I_z = Symbol("I_x"), Symbol("I_y"), Symbol("I_z")
In [2]: S_x, S_y, S_z = Symbol("S_x"), Symbol("S_y"), Symbol("S_z")
In [3]: J_is = Symbol("J_IS")
In [4]: t = Symbol("t")
In [5]: substitutions = (
(2 * I_x * S_z, 2 * I_x * S_z * cos(2 * pi * J_is * t) + I_y * sin(2 * pi * J_is * t)),
(I_x, I_x * cos(2 * pi * J_is * t) + 2 * I_x * S_z * sin(2 * pi * J_is * t)),
(I_y, I_y * cos(2 * pi * J_is * t) - 2 * I_x * S_z * sin(2 * pi * J_is * t))
)
In [6]: (2 * I_x * S_z).subs(substitutions)
Out[7]: (I_y*cos(2*pi*J_IS*t) - 2*I_x*S_z*sin(2*pi*J_IS*t))*sin(2*pi*J_IS*t) + 2*S_z*(I_x*cos(2*pi*J_IS*t) + 2*I_x*S_z*sin(2*pi*J_IS*t))*cos(2*pi*J_IS*t)
Only the appropriate substitution should happen, in this case only the first one. So the expected output should be the following:
In [6]: (2 * I_x * S_z).subs(substitutions)
Out[7]: I_y*sin(2*pi*J_IS*t) + 2*I_x*S_z*cos(2*pi*J_IS*t)
The current version of sympy provides the keyword simultaneous. The complicated operations in the previous answers are no more necessary:
In [1]: (x*sin(y)).subs([(x,y),(y,x)],simultaneous=True)
Out[1]: y⋅sin(x)
The subs(self,*args) method is defined (in part) this way:
In [11]: x.subs??
...
sequence = args[0]
if isinstance(sequence, dict):
return self._subs_dict(sequence)
elif isinstance(sequence, (list, tuple)):
return self._subs_list(sequence)
If you pass subs a dict, you lose control over the order of the substitutions.
While if you pass subs a list or tuple, you can control the order.
This doesn't allow you to do simultaneous substitutions. That would lead to difficulties if the user were to pass stuff like x.subs([(x,y),(y,x)]). So I doubt sympy has a method for doing simultaneous substitutions. Instead I believe all substutions are either unordered (if you pass a dict) or, at best, done by a 1-pass ordered substitution (if you pass a list or tuple):
In [17]: x.subs([(x,y),(y,z)])
Out[18]: z
In [19]: x.subs([(y,z),(x,y)])
Out[19]: y
PS. _subs_list(self, sequence) is defined (in part) like this:
In [14]: x._subs_list??
...
for old, new in sequence:
result = result.subs(old, new)
This nails down the order in which the subs are done.
Example for #~unutbu's answer:
>>> import ordereddict # collections.OrderedDict in Python 2.7+
>>> from sympy import *
>>> x,y,z = symbols('xyz')
>>> x.subs(ordereddict.OrderedDict([(x,y),(y,z)]))
y
>>> x.subs(ordereddict.OrderedDict([(y,z),(x,y)]))
z
Answering the edited question.
In your example you can use some temporary variables which will not be over-written be subsequent substitutions. Then, once all of the potentially overlapping substitutions have been made, you can replace the temporary variables with the real ones.
This example works for the question, if your full problem contains more complex substitutions, I think you should still be able to create temporary variables to avoid overlapping substitutions.
from sympy import Symbol, sin, cos, pi
I_x, I_y, I_z = Symbol("I_x"), Symbol("I_y"), Symbol("I_z")
S_x, S_y, S_z = Symbol("S_x"), Symbol("S_y"), Symbol("S_z")
J_is = Symbol("J_IS")
t = Symbol("t")
I_x_temp, I_y_temp, I_z_temp = Symbol("I_x_temp"), Symbol("I_y_temp"), Symbol("I_z_temp")
f = 2*I_x*S_z
answer = I_y*sin(2*pi*J_is*t) + 2*I_x*S_z*cos(2*pi*J_is*t)
subs1a = [
(2*I_x*S_z, 2*I_x_temp*S_z*cos(2*pi*J_is*t) + I_y_temp*sin(2*pi*J_is*t)),
(I_x, I_x_temp*cos(2* pi*J_is*t) + 2*I_x_temp*S_z*sin(2*pi*J_is*t)),
(I_y, I_y_temp*cos(2*pi*J_is*t) - 2*I_x_temp*S_z* sin(2*pi*J_is*t))
]
subs_temp = [(I_x_temp, I_x), (I_y_temp, I_y), (I_z_temp, I_z)]
print f
f = f.subs(subs1a)
print f
f = f.subs(subs_temp)
print f
print f == answer # True
Note, you can also perform two substitutions back to back:
f.subs(subs1a).subs(subs_temp) == answer
The Keyword simultaneous will do non-clashing subs regardless of the input (dict or sequence):
>>> x.subs([(x,y),(y,z)],simultaneous=1)
y
>>> x.subs([(y,z),(x,y)],simultaneous=1)
y

Categories