Print FormatList Issue - python

I'm just having a small issue of formatting right now. My current code prints out my out in a wonky way and I'm trying to make it look smoother. How would I change my print formatting?
height = {}
length = len(preys)
rank = 0
while preys != [None]*length:
for index,(animal,prey) in enumerate(zip(animals,preys)):
if prey not in animals:
try:
if height[prey] < rank:
height[prey] = rank
except KeyError:
height[prey] = 0
height[animal] = height[prey] + 1
preys[index] = None
animals[index] = None
rank += 1
for arg in sys.argv:
print (sorted (height.items(),key = lambda x:x[1],reverse=True))
if name == "main":
main()
The output looks like this:
[('Lobster', 4), ('Bird', 4), ('Fish', 3), ('Whelk', 3), ('Crab', 3), ('Mussels', 2), ('Prawn', 2), ('Zooplankton', 1), ('Limpets', 1), ('Phytoplankton', 0), ('Seaweed', 0)]
and I'm trying to make it look like:
Heights:
Bird: 4
Crab: 3
Fish: 3
Limpets: 1
Lobster: 4
Mussels: 2
Phytoplankton: 0
Prawn: 2
Seaweed: 0
Whelk: 3
Zooplankton: 1
I've attempted to use the: print(formatList(height)), format however printing anything before the "height" causes errors

Since the output only prints once, we know that sys.argv wasn't passed any extra arguments. There's no need to create a loop that will only be executed once (and, if there were multiple arguments, it isn't generally useful to print the same output several times). Instead, loop over the height object itself.
You are also currently sorting by value, when it's obvious that you want to sort by key. Since the latter is the default sort anyway, I'm not sure why you added (messy) code to sort by value.
Use string formatting to express the appearance you want for each item.
print('Heights:')
for item in sorted(height.items()):
print('{}: {}'.format(*item))
Note that sorting an iterable of tuple objects will sort by the first element first, and then, if there are two tuples with the same first item, they will be sorted by the second element. For example, ('Bird', 1) would come before ('Bird', 2). Since dictionaries can't have duplicate keys, this won't be an issue here, but it's something to keep in mind.

I think you can try something like this:
sorted_ list = sorted (height.items(),key = lambda x:x[1],reverse=True)
print(''.join('%s : %s\n' % x for x in sorted_list))
Or in one expression(if it is not ugly for you):
print(''.join('%s : %s\n' % x
for x in sorted (height.items(),key = lambda x:x[1],reverse=True)))

Related

Why is my backtracking algorithm not working for this simple problem?

Hi I am currently trying to solve the following problem:
Suppose you have a random list of people standing in a queue. Each person is described by a pair of integers (h, k), where h is the height of the person and k is the number of people in front of this person who have a height greater than or equal to h. Write an algorithm to reconstruct the queue.
Note:
The number of people is less than 1,100.
Example
Input:
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]
Output:
[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]
Here is what I have so far as my solution:
answer = []
def reconstructQueue(people):
if len(answer) == len(people) and is_valid(answer):
return True
for person in people:
answer.append(person)
if is_valid(answer):
reconstructQueue([x for x in people if x != person])
else:
answer.pop()
def is_valid(answer):
answer = answer[::-1]
for i in range(len(answer)):
count = 0
for j in answer[i+1:]:
if answer[i][0] <= j[0]:
count+=1
if count != answer[i][1]:
return False
return True
My is_valid function is working but the backtracking implemented in recontructQueue is going wrong!
Here is how I think it should work:
Checks if we have a valid answer that contains all the original list values, if so we are done.
If not then add someone else to the list answer and see if that is valid.
If it is valid then recursively call the function removing the element we just added.
If it is not valid then we have made a mistake, so we backtrack by popping that element from the answer.
It is not giving me the correct answer though.
Can someone help explain where I am going wrong, I am terrible at backtracking!
Thank you.
Having a global answer as part of your recursive inner workings isn't a great idea. We'll replace it with a defaulted second argument.
As #MattTimmermans notes, you've made the recursion beginner's error of having your recursive function return a useful result but then ignoring that result when you call it recursively. That recursive call will result in success or failure and you need to act accordingly:
def reconstructQueue(people, todo=None):
if not todo and is_valid(people):
return people
if todo is None:
todo = people
people = []
tossed = []
while todo:
people.append(todo.pop())
if is_valid(people):
result = reconstructQueue(people, todo + tossed)
if result:
return result
tossed.append(people.pop())
return None # there is no solution from this starting point
def is_valid(answer):
for index, (my_height, my_count) in enumerate(answer):
their_count = sum(my_height <= their_height for their_height, _ in answer[:index])
if their_count != my_count:
return False
return True
scrambled = [(7, 0), (4, 4), (7, 1), (5, 0), (6, 1), (5, 2)]
print(reconstructQueue(scrambled))
I've also switched your data structure from a list of lists to a list of tuples as that makes more sense to me Python-wise.
OUTPUT
> python3 test.py
[(5, 0), (7, 0), (5, 2), (6, 1), (4, 4), (7, 1)]
>

Python Comprehension - Replace Nested Loop

Working on the 'two-sum' problem..
Input: An unsorted array A (of integers), and a target sum t
The goal: to return a list of tuple pairs (x,y) where x + y = t
I've implemented a hash-table H to store the contents of A. Through use of a nested loop to iterate through H, I'm achieving the desired output. However, in the spirit of learning the art of Python, I'd like to replace the nested loop with a nice 1-liner using comprehension & a maybe lambda function? Suggestions?
Source Code:
import csv
with open('/Users/xxx/Developer/Algorithms/Data Structures/_a.txt') as csvfile:
csv_reader = csv.reader(csvfile, delimiter ='\n')
hash_table = {int(num[0]):int(num[0]) for(num) in csv_reader} #{str:int}
def two_sum(hash_table, target):
pairs = list()
for x in hash_table.keys():
for y in hash_table.keys():
if x == y:
continue
if x + y == target:
pairs.append((x,y))
return pairs
When you have two ranges and you want to loop both of them separately to get all the combinations as in your case, you can combine the loops into one using itertools.product. You can replace the code below
range1 = [1,2,3,4]
range2 = [3, 4, 5]
for x in range1:
for y in range2:
print(x, y)
with
from itertools import product
for x, y in product(range1, range2):
print(x, y)
Both code blocks produce
1 3
1 4
1 5
2 3
2 4
2 5
3 3
3 4
3 5
4 3
4 4
4 5
But you would still need the if check with this construct. However, what product returns is a generator and you can pass that as the iterable to map or filter along with a lambda function.
In your case you only want to include pairs that meet the criteria. Thus, filter is what you want. In my simple example, if we only want combinations whose sum is even, then we could do something like
gen = product(range1, range2)
f = lambda i: (i[0] + i[1]) % 2 == 0
desired_pairs = filter(f, gen)
This can be written as a one-liner like
desired_pairs = filter(lambda i: (i[0] + i[1]) % 2 == 0, product(range1, range2))
without being too complicated for being understood.
Note that like product and map, what filter returns is a generator, which is good if you are just going to loop over it later to do some other work. If you really need a list just do convert it to a list as
desired_pairs = list(filter(lambda i: (i[0] + i[1]) % 2 == 0, product(range1, range2)))
If we print this we get
[(1, 3), (1, 5), (2, 4), (3, 3), (3, 5), (4, 4)]

How to sort a list in a very specific way in Python?

How can I do a very explicit sort on a list in Python? What I mean is, items are supposed to be sorted a very specific way and not just alphabetically or numerically. The input I would be receiving looks something list this:
h43948fh4349f84 ./.file.html
dsfj940j90f94jf ./abcd.ppt
f9j3049fj349f0j ./abcd_FF_000000001.jpg
f0f9049jf043930 ./abcd_FF_000000002.jpg
j909jdsa094jf49 ./abcd_FF_000000003.jpg
jf4398fj9348fjj ./abcd_FFinit.jpg
9834jf9483fj43f ./abcd_MM_000000001.jpg
fj09jw93fj930fj ./abcd_MM_000000002.jpg
fjdsjfd89s8hs9h ./abcd_MM_000000003.jpg
vyr89r8y898r839 ./abcd_MMinit.jpg
The list should be sorted:
html file first
ppt file second
FFinit file third
MMinit file fourth
The rest of the numbered files in the order of FF/MM
Example output for this would look like:
h43948fh4349f84 ./.file.html
dsfj940j90f94jf ./abcd.ppt
jf4398fj9348fjj ./abcd_FFinit.jpg
vyr89r8y898r839 ./abcd_MMinit.jpg
f9j3049fj349f0j ./abcd_FF_000000001.jpg
9834jf9483fj43f ./abcd_MM_000000001.jpg
f0f9049jf043930 ./abcd_FF_000000002.jpg
fj09jw93fj930fj ./abcd_MM_000000002.jpg
j909jdsa094jf49 ./abcd_FF_000000003.jpg
fjdsjfd89s8hs9h ./abcd_MM_000000003.jpg
You need to define a key function, to guide the sorting. When comparing values to see what goes where, the result of the key function is then used instead of the values directly.
The key function can return anything, but here a tuple would be helpful. Tuples are compared lexicographically, meaning that only their first elements are compared unless they are equal, after which the second elements are used. If those are equal too, further elements are compared, until there are no more elements or an order has been determined.
For your case, you could produce a number in the first location, to order the 'special' entries, then for the remainder return the number in the second position and the FF or MM string in the last:
def key(filename):
if filename.endswith('.html'):
return (0,) # html first
if filename.endswith('.ppt'):
return (1,) # ppt second
if filename.endswith('FFinit.jpg'):
return (2,) # FFinit third
if filename.endswith('MMinit.jpg'):
return (3,) # MMinit forth
# take last two parts between _ characters, ignoring the extension
_, FFMM, number = filename.rpartition('.')[0].rsplit('_', 2)
# rest is sorted on the number (compared here lexicographically) and FF/MM
return (4, number, FFMM)
Note that the tuples don't need to be of equal length even.
This produces the expected output:
>>> from pprint import pprint
>>> lines = '''\
... h43948fh4349f84 ./.file.html
... dsfj940j90f94jf ./abcd.ppt
... f9j3049fj349f0j ./abcd_FF_000000001.jpg
... f0f9049jf043930 ./abcd_FF_000000002.jpg
... j909jdsa094jf49 ./abcd_FF_000000003.jpg
... jf4398fj9348fjj ./abcd_FFinit.jpg
... 9834jf9483fj43f ./abcd_MM_000000001.jpg
... fj09jw93fj930fj ./abcd_MM_000000002.jpg
... fjdsjfd89s8hs9h ./abcd_MM_000000003.jpg
... vyr89r8y898r839 ./abcd_MMinit.jpg
... '''.splitlines()
>>> pprint(sorted(lines, key=key))
['h43948fh4349f84 ./.file.html',
'dsfj940j90f94jf ./abcd.ppt',
'jf4398fj9348fjj ./abcd_FFinit.jpg',
'vyr89r8y898r839 ./abcd_MMinit.jpg',
'f9j3049fj349f0j ./abcd_FF_000000001.jpg',
'9834jf9483fj43f ./abcd_MM_000000001.jpg',
'f0f9049jf043930 ./abcd_FF_000000002.jpg',
'fj09jw93fj930fj ./abcd_MM_000000002.jpg',
'j909jdsa094jf49 ./abcd_FF_000000003.jpg',
'fjdsjfd89s8hs9h ./abcd_MM_000000003.jpg']
You can use the key argument to sort(). This method of the list class accepts an element of the list and returns a value that can be compared to other return values to determine sorting order. One possibility is to assign a number to each criteria exactly as you describe in your question.
Use sorted and a custom key function.
strings = ['h43948fh4349f84 ./.file.html',
'dsfj940j90f94jf ./abcd.ppt',
'f9j3049fj349f0j ./abcd_FF_000000001.jpg',
'f0f9049jf043930 ./abcd_FF_000000002.jpg',
'j909jdsa094jf49 ./abcd_FF_000000003.jpg',
'jf4398fj9348fjj ./abcd_FFinit.jpg',
'9834jf9483fj43f ./abcd_MM_000000001.jpg',
'fj09jw93fj930fj ./abcd_MM_000000002.jpg',
'fjdsjfd89s8hs9h ./abcd_MM_000000003.jpg',
'vyr89r8y898r839 ./abcd_MMinit.jpg']
def key(string):
if string.endswith('html'):
return 0,
elif string.endswith('ppt'):
return 1,
elif string.endswith('FFinit.jpg'):
return 2,
elif string.endswith('MMinit.jpg'):
return 3,
elif string[-16:-14] == 'FF':
return 4, int(string[-13:-4]), 0
elif string[-16:-14] == 'MM':
return 4, int(string[-13:-4]), 1
result = sorted(strings, key=key)
for string in result:
print(string)
Out:
h43948fh4349f84 ./.file.html
dsfj940j90f94jf ./abcd.ppt
jf4398fj9348fjj ./abcd_FFinit.jpg
vyr89r8y898r839 ./abcd_MMinit.jpg
f9j3049fj349f0j ./abcd_FF_000000001.jpg
9834jf9483fj43f ./abcd_MM_000000001.jpg
f0f9049jf043930 ./abcd_FF_000000002.jpg
fj09jw93fj930fj ./abcd_MM_000000002.jpg
j909jdsa094jf49 ./abcd_FF_000000003.jpg
fjdsjfd89s8hs9h ./abcd_MM_000000003.jpg
I assumed the last ordering point just looked at the number before the file extension (e.g. 000001)
def custom_key(x):
substring_order = ['.html','.ppt','FFinit','MMinit']
other_order = lambda x: int(x.split('_')[-1].split('.')[0])+len(substring_order)
for i,o in enumerate(substring_order):
if o in x:
return i
return other_order(x)
sorted_list = sorted(data,key=custom_key)
import pprint
pprint.pprint(sorted_list)
Out:
['h43948fh4349f84 ./.file.html',
'dsfj940j90f94jf ./abcd.ppt',
'jf4398fj9348fjj ./abcd_FFinit.jpg',
'vyr89r8y898r839 ./abcd_MMinit.jpg',
'f9j3049fj349f0j ./abcd_FF_000000001.jpg',
'9834jf9483fj43f ./abcd_MM_000000001.jpg',
'f0f9049jf043930 ./abcd_FF_000000002.jpg',
'fj09jw93fj930fj ./abcd_MM_000000002.jpg',
'j909jdsa094jf49 ./abcd_FF_000000003.jpg',
'fjdsjfd89s8hs9h ./abcd_MM_000000003.jpg']

Python insert missing elements in array

I have an array in the following format:
[(u'iapsetting', 0), (u'iap-mms',0)]
The array must contain only tuples of this type:
(u'mmssetting', 0) or (u'iapsetting', 0) or (u'iap-mms', 0)
My first array is obviously missing the (u'mmssetting',0) but it could many different combinations of missing/existing tuples.
I am struggling to find a way to add the correct missing tuples. The array must always contain only 3 tuples.
This is what I have so far but it is not working as expected:
for type in setup: #setup is the array containing 1,2 or 3 tuples
iap_found = False
mms_found = False
iap_mms_found = False
if type[0]=='iapsetting':
iap_found = True
elif type[0]=='mmssetting':
mms_found = True
elif type[0]== 'iap-mms':
iap_mms_found = True
#Add missing settings
if(iap_found==False):
print("MISSING IAP",setup)
setup.append((u'iapsetting',0))
elif(mms_found==False):
print("MISSING MMS",setup)
setup.append((u'mmssetting',0))
elif(iap_mms_found==False):
print("MISSING IAP-MMS",setup)
setup.append((u'iap-mms',0))
Any help will be greatly appreciated because there might be a much better way of doing this. Thank you.
Sara
Try this:
existing = set(x[0] for x in setup)
for expected in ('iapsetting', 'mmssetting', 'iap-mms'):
if expected not in existing:
setup.append((expected, 0))
What you were doing wrong were mainly two things:
you initialized the flags inside the loop.
you started adding missing settings before you finished looping through the whole array.
# Initialize flags before entering loop:
iap_found = False
mms_found = False
iap_mms_found = False
for type in setup: #setup is the array containing 1,2 or 3 tuples
if type[0]=='iapsetting':
iap_found = True
elif type[0]=='mmssetting':
mms_found = True
elif type[0]== 'iap-mms':
iap_mms_found = True
#Add missing settings after looping through the whole array:
if(iap_found==False):
print("MISSING IAP",setup)
setup.append((u'iapsetting',0))
if(mms_found==False):
print("MISSING MMS",setup)
setup.append((u'mmssetting',0))
if(iap_mms_found==False):
print("MISSING IAP-MMS",setup)
setup.append((u'iap-mms',0))
Instead of using if ... elif .. elif you should use
if not iap_found:
print("MISSING IAP",setup)
setup.append((u'iapsetting',0))
if not mms_found:
print("MISSING MMS",setup)
setup.append((u'mmssetting',0))
if not iap_mms_found==False:
print("MISSING IAP-MMS",setup)
setup.append((u'iap-mms',0))
The problem of if .. elif is that only one branch will get executed.
For other solutions, different (and in this case also better) than if, see the other anwers
I think sets represent this logic most clearly. Use a set comprehension to extract the strings.
REQUIRED_VALUES = {"mmssetting", "iapsetting", "iap-mms"}
input_tuple_list = [(u'mmssetting', 1), (u'iap-mms', 3)]
# set comprehension paired with set difference -- results in {"iapsetting"}
missing_values = REQUIRED_VALUES - {val for val, count in input_tuple_list}
# results in [(u'mmssetting', 1), (u'iap-mms', 3), (u'iapsetting', 3)]
input_tuple_list.extend((val, 0) for val in missing_values)

Getting the endpoints of sets across a range

For the life of me, I can't see how to do this. I need to collect the non-overlapping endpoints of several sets within a range of numbers with python.
For example the user could input a range of 10 and two sets 2 and 3. I need to get the end points of these sets within this range such that:
set 2 groupings: 1-2,6-7
set 3 groupings: 3-5,8-10
The range, number of sets, and size of any individual set is arbitrary. I cannot fall outside the range, so no half sets.
I keep thinking there should be a simple formula for this, but I can't come up with it.
Edit
As requested for an example input of range 12, and sets 1, 2, and 3 the output should be:
set 1: 1,7
set 2: 2-3,8-9
set 3: 4-6,10-12
As near as I can figure, I'm looking at some kind of accumulator pattern. Something like this psuedo code:
for each miniRange in range:
for each set in sets:
listOfCurrSetEndpoints.append((start, end))
I don't think there's a good built-in solution to this. (It would be easier if there were a built-in equivalent to Haskell's scan function.) But this is concise enough:
>>> import itertools
>>> from collections import defaultdict
>>> partition_lengths = [1, 2, 3]
>>> range_start = 1
>>> range_end = 12
>>> endpoints = defaultdict(list)
>>> for p_len in itertools.cycle(partition_lengths):
... end = range_start + p_len - 1
... if end > range_end: break
... endpoints[p_len].append((range_start, end))
... range_start += p_len
...
>>> endpoints
defaultdict(<type 'list'>, {1: [(1, 1), (7, 7)], 2: [(2, 3), (8, 9)], 3: [(4, 6), (10, 12)]})
You can now format the endpoints dictionary for output however you like.
As an aside, I'm really confused by your use of "set" in this question, which is why I used "partition" instead.
I'm not exactly happy with it, but I did get a working program. If someone can come up with a better answer, I'll be happy to accept it instead.
import argparse, sys
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Take a number of pages, and the pages in several sets, to produce an output for copy and paste into the print file downloader', version='%(prog)s 2.0')
parser.add_argument('pages', type=int, help='Total number of pages to break into sets')
parser.add_argument('stapleset', nargs='+', type=int, help='number of pages in each set')
args = parser.parse_args()
data = {}
for c,s in enumerate(args.stapleset):
data[c] = []
currPage = 0
while currPage <= args.pages:
for c,s in enumerate(args.stapleset):
if currPage + 1 > args.pages:
pass
elif currPage + s > args.pages:
data[c].append((currPage+1,args.pages))
else:
data[c].append((currPage+1,currPage+s))
currPage = currPage + s
for key in sorted(data.iterkeys()):
for c,t in enumerate(data[key]):
if c > 0:
sys.stdout.write(",")
sys.stdout.write("{0}-{1}".format(t[0],t[1]))
sys.stdout.write("\n\n")

Categories

Resources