I'm doing a simple task which requires to sort a list by expression result and running this code:
def sort_by_answer(lst):
ans = []
dict = {}
for i in lst:
if 'x' in i:
i = i.replace('x', '*')
dict.update({i: eval(i)})
dict = {k: v for k, v in sorted(dict.items(), key=lambda item: item[1])}
res = list(dict.keys())
for i in res:
if '*' in i:
i = i.replace('*', 'x')
ans.append(i)
else:
ans.append(i)
return ans
It checks out but the site for which i'm doing this test(here's a link to the task(https://edabit.com/challenge/9zf6scCreprSaQAPq) tells my that my list is not correctly sorted, which it is, can someone help me improve this code or smth so it works in every case-scenario?
P.S.
if 'x' in i:
i = i.replace('x', '*')
This is made so i can use the eval function but the site input has 'x' instead of '*' in their lists..
You can try this. But using eval is dangerous on untrusted strings.
In [63]: a=['1 + 1', '1 + 7', '1 + 5', '1 + 4']
In [69]: def evaluate(_str):
...: return eval(_str.replace('x','*'))
output
In [70]: sorted(a,key=evaluate)
Out[70]: ['1 + 1', '1 + 4', '1 + 5', '1 + 7']
In [71]: sorted(['4 - 4', '2 - 2', '5 - 5', '10 - 10'],key=evaluate)
Out[71]: ['4 - 4', '2 - 2', '5 - 5', '10 - 10']
In [72]: sorted(['2 + 2', '2 - 2', '2 x 1'],key=evaluate)
Out[72]: ['2 - 2', '2 x 1', '2 + 2']
I don't think it is an issue with your code, probably they are using something older that 3.6 and it is messing up the order of the dict. A tuple would be safer.
def sort_by_answer(lst):
string = ','.join(lst).replace('x','*')
l = string.split(',')
d = [(k.replace('*','x'), eval(k)) for k in l]
ans = [expr for expr, value in sorted(d, key = lambda x: x[1])]
return ans
EDIT:
#Ch3steR's answer is more pythonic:
def sort_by_answer(lst):
return sorted(lst, key= lambda x: eval(x.replace('x','*')))
Related
This is my method. I am having trouble with returning the entire dictionary
def get_col(amount):
letter = 0
value = []
values = {}
for i in range(amount):
letter = get_column_letter(i + 1)
[value.append(row.value) for row in ws[letter]]
values = dict(zip(letter, [value]))
value = []
return values
I want it to output it like this:
{'A': ['ID', 'value is 1', 'value is 2', 'value is 3', 'value is 4', 'value is 5', 'value is 6']}
{'B': ['Name', 'value is 1', 'value is 2', 'value is 3', 'value is 4', 'value is 5', 'value is 6']}
{'C': ['Math', 'value is 1', 'value is 2', 'value is 3', 'value is 4', 'value is 5', 'value is 6']}
But when the return is onside the 'for' it only returns
{'A': ['ID', 'value is 1', 'value is 2', 'value is 3', 'value is 4', 'value is 5', 'value is 6']}
and when the return is outside the 'for' loop, it returns
{'C': ['Math', 'value is 1', 'value is 2', 'value is 3', 'value is 4', 'value is 5', 'value is 6']}
Any help would be appreciated. Thank you!
I am assuming you want all of the data in one dictionary:
values = dict(zip(letter, [value]))
Currently this part of your code overites the dictionary everytime. It is why you get the "A" dict with returning before the for loop finishes, and why after the loop finishes when return the dict is only the "C" dict as the "A" and "B" were overwriten.
Put the return outside the for loop afterwards, and instead of
values = dict(zip(letter, [value]))
use
values[letter] = value
as this will append more keys/values to the dict.
ps. This is my first post, I hope it helps and is understandable.
edit: If you are wanting a list of three dictionaries like your desired output shows do this:
def get_col(amount):
letter = 0
value = []
values = []
for i in range(amount):
letter = get_column_letter(i + 1)
[value.append(row.value) for row in ws[letter]]
values.append(dict(zip(letter, [value])))
value = []
return values
Your desired output is not a single dictionary. It's a list of dictionaries.
In the for loop, at each iteration you are creating a new dictionary. When you return, you either return the first one you create or the last one if you put the return inside or outside respectevely.
You need to return a list of the created dictionaries
def get_col(amount):
letter = 0
value = []
values = {}
values_list = []
for i in range(amount):
letter = get_column_letter(i + 1)
[value.append(row.value) for row in ws[letter]]
values = dict(zip(letter, [value]))
value = []
values_list.append(values)
return values_list
I'm trying to add values from List2 if the type is the same in List1. All the data is strings within lists. This isn't the exact data I'm using, just a representation. This is my first programme so please excuse any misunderstandings.
List1 = [['Type A =', 'Value 1', 'Value 2', 'Value 3'], ['Type B =', 'Value 4', 'Value 5']]
List2 = [['Type Z =', 'Value 6', 'Value 7', 'Value 8'], ['Type A =', 'Value 9', 'Value 10', 'Value 11'], ['Type A =', 'Value 12', 'Value 13']]
Desired result:
new_list =[['Type A =', 'Value 1', 'Value 2', 'Value 3', 'Value 9', 'Value 10', 'Value 11', 'Value 12', 'Value 13'], ['Type B =', 'Value 4', 'Value 5']]
Current attempt:
newlist = []
for values in List1:
for valuestoadd in List2:
if values[0] == valuestoadd[0]:
newlist = [List1 + [valuestoadd[1:]]]
else:
print("Types don't match")
return newlist
This works for me if there weren't two Type A's in List2 as this causes my code to create two instances of List1. If I was able to add the values at a specific index of the list then that would be great but I can work around that.
It's probably easier to use a dictionary for this:
def merge(d1, d2):
return {k: v + d2[k] if k in d2 else v for k, v in d1.items()}
d1 = {'A': [1, 2, 3], 'B': [4, 5, 6]}
d2 = {'A': [7, 8, 9], 'C': [0]}
print(merge(d1, d2))
If you must use a list, it's fairly easy to temporarily convert to a dictionary and back to a list:
from collections import defaultdict
def list_to_dict(xss):
d = defaultdict(list)
for xs in xss:
d[xs[0]].extend(xs[1:])
return d
def dict_to_list(d):
return [[k, *v] for k, v in d.items()]
Rather than using List1 + [valuestoadd[1:]], you should be using newlist[0].append(valuestoadd[1:]) so that it doesn't ever create a new list and only appends to the old one. The [0] is necessary so that it appends to the first sublist rather than the whole list.
newlist = List1 #you're doing this already - might as well initialize the new list with this code
for values in List1:
for valuestoadd in List2:
if values[0] == valuestoadd[0]:
newlist[0].append(valuestoadd[1:]) #adds the values on to the end of the first list
else:
print("Types don't match")
Output:
[['Type A =', 'Value 1', 'Value 2', 'Value 3', ['Value 9', 'Value 10', 'Value 11'], ['Value 12', 'Value 13']], ['Type B =', 'Value 4', 'Value 5']]
This does, sadly, input the values as a list - if you want to split them into individual values, you would need to iterate through the lists you're adding on, and append individual values to newlist[0].
This could be achieved with another for loop, like so:
if values[0] == valuestoadd[0]:
for subvalues in valuestoadd[1:]: #splits the list into subvalues
newlist[0].append(subvalues) #appends those subvalues
Output:
[['Type A =', 'Value 1', 'Value 2', 'Value 3', 'Value 9', 'Value 10', 'Value 11', 'Value 12', 'Value 13'], ['Type B =', 'Value 4', 'Value 5']]
I agree with the other answers that it would be better to use a dictionary right away. But if you want, for some reason, stick to the data structure you have, you could transform it into a dictionary and back:
type_dict = {}
for tlist in List1+List2:
curr_type = tlist[0]
type_dict[curr_type] = tlist[1:] if not curr_type in type_dict else type_dict[curr_type]+tlist[1:]
new_list = [[k] + type_dict[k] for k in type_dict]
In the creation of new_list, you can take the keys from a subset of type_dict only if you do not want to include all of them.
According to the documentation, you can provide a linejunk function to ignore certian lines. However, I can't get it to work. Here is some sample code for discussion:
from re import search
from difflib import ndiff
t1 = 'one 1\ntwo 2\nthree 3'
t2 = 'one 1\ntwo 29\nthree 3'
diff = ndiff(t1.splitlines(), t2.splitlines(), lambda x: search('2', x))
My intention is to ignore the second line and diff will be a generator that doesn't show any differences.
Thanks for the help.
I've recently met with the same problem.
Here's what I've found out:
cf. http://bugs.python.org/issue14332
The main intent of the *junk parameters is to speed up matching to
find differences, not to mask differences.
c.f.
http://hg.python.org/cpython/rev/0a69b1e8b7fe/
The patch provides a better explanation of the "junk" and "ignore" concepts in difflib docs
These junk-filtering functions speed up matching to find differences
and do not cause any differing lines or characters to be ignored.
Your example has a problem: the first two arguments to ndiff should each be a list of strings; you have a single string which is treated just like a list of characters. See the docs. Use e.g. t1 = 'one 1\ntwo 2\nthree 3'.splitlines()
However as the following example shows, difflib.ndiff doesn't call the linejunk function for all lines. This is longstanding behaviour -- verified with Python 2.2 to 2.6 inclusive, and 3.1.
Example script:
from difflib import ndiff
t1 = 'one 1\ntwo 2\nthree 3'.splitlines()
t2 = 'one 1\ntwo 29\nthree 3'.splitlines()
def lj(line):
rval = '2' in line
print("lj: line=%r, rval=%s" % (line, rval))
return rval
d = list(ndiff(t1, t2 )); print("%d %r\n" % (1, d))
d = list(ndiff(t1, t2, lj)); print("%d %r\n" % (2, d))
d = list(ndiff(t2, t1, lj)); print("%d %r\n" % (3, d))
Output from running with Python 2.6:
1 [' one 1', '- two 2', '+ two 29', '? +\n', ' three 3']
lj: line='one 1', rval=False
lj: line='two 29', rval=True
lj: line='three 3', rval=False
2 [' one 1', '- two 2', '+ two 29', '? +\n', ' three 3']
lj: line='one 1', rval=False
lj: line='two 2', rval=True
lj: line='three 3', rval=False
3 [' one 1', '- two 29', '? -\n', '+ two 2', ' three 3']
You may wish to report this as a bug. However note that the docs don't say explicitly what meaning is attached to lines that are "junk". What output were you expecting?
Further puzzlement: adding these lines to the script:
t3 = 'one 1\n \ntwo 2\n'.splitlines()
t4 = 'one 1\n\n#\n\ntwo 2\n'.splitlines()
d = list(ndiff(t3, t4 )); print("%d %r\n" % (4, d))
d = list(ndiff(t4, t3 )); print("%d %r\n" % (5, d))
d = list(ndiff(t3, t4, None)); print("%d %r\n" % (6, d))
d = list(ndiff(t4, t3, None)); print("%d %r\n" % (7, d))
produces this output:
4 [' one 1', '- ', '+ ', '+ #', '+ ', ' two 2']
5 [' one 1', '+ ', '- ', '- #', '- ', ' two 2']
6 [' one 1', '- ', '+ ', '+ #', '+ ', ' two 2']
7 [' one 1', '+ ', '- ', '- #', '- ', ' two 2']
In other words the result when using the default linejunk function is the same as not using a linejunk function, in a case containing different "junk" lines (whitespace except for an initial hash).
Perhaps if you could tell us what you are trying to achieve, we might be able to suggest an alternative approach.
Edit after further info
If your intention is in generality to ignore all lines containing '2', meaning pretend that they don't exist for ndiff purposes, all you have to do is turn the pretence into reality:
t1f = [line for line in t1 if '2' not in line]
t2f = [line for line in t2 if '2' not in line]
diff = ndiff(t1f, t2f)
I want to extract the string from my list. This is my list.
[['Lions 3', ' Snakes 3'], ['Tarantulas 1', ' FC Awesome 0'],
['Lions 1', ' FC Awesome 1'], ['Tarantulas 3', ' Snakes 1'],
['Lions 4', ' Grouches 0']]
This is what I have tried:
The if statement is inside a for loop to iterate through the list.
if items[0][-1] == items[1][-1]:
print('Draw for {} & {}'.format(items[0][:4], items[1][1:]))
Output:
Draw for Lions 3 & Snakes 3
Desired output:
Draw for Lions & Snakes
without the integers
You can use
import re
reg_exp = re.compile(r"[A-Za-z]+")
my_string = "String 3"
reg_exp.search(my_string).group(0) // outputs 'String'
of course you need to adapt this to your loop to extract the desired string
Splitting would grab all the digits, not just last and help you handle high scores such as 11, 12 ... . Right split helps handle multiword teams like 'FC Awesome'.
for left, right in lst:
lteam, lscore = left.strip().rsplit(' ', 1)
rteam, rscore = right.strip().rsplit(' ', 1)
if lscore == rscore:
print('Draw for {} & {}'.format(lteam, rteam))
Strip deletes trailing or initial spaces.
This is what you can try.
>>>
>>> items = [['Lions 3', ' Snakes 3'], ['Tarantulas 1', ' FC Awesome 0'], ['Lions 1', ' FC Awesome 1'], ['Tarantulas 3', ' Snakes 1'], ['Lions 4', ' Grouches 0']]
>>>
>>> output = [[s.split()[0] for s in item] for item in items]
>>> output
[['Lions', 'Snakes'], ['Tarantulas', 'FC'], ['Lions', 'FC'], ['Tarantulas', 'Snakes'], ['Lions', 'Grouches']]
>>>
Finally, this is what you can try to get your o/p.
>>> for item in output:
... print('Draw for {} & {}'.format(item[0], item[1]))
...
Draw for Lions & Snakes
Draw for Tarantulas & FC
Draw for Lions & FC
Draw for Tarantulas & Snakes
Draw for Lions & Grouches
>>>
Jumping to some conclusions:
matches = [['Lions 3', 'Snakes 3'],
['Tarantulas 1', 'FC Awesome 0'],
['Lions 1', 'FC Awesome 1'],
['Tarantulas 3', 'Snakes 1'],
['Lions 4', 'Grouches 0']]
def split_team_and_score(team_and_score):
team, _, raw_score = team_and_score.rpartition(" ")
return team, int(raw_score)
for team_and_score_1, team_and_score_2 in matches:
team1, score1 = split_team_and_score(team_and_score_1)
team2, score2 = split_team_and_score(team_and_score_2)
if score1 == score2:
print('Draw for {} & {}'.format(team1, team2))
I want to generate a large number of key value pairs to put in my dictionary using a for loop. For example, the dictionary looks like this:
my_dict = dict()
my_dict["r0"] = "tag 0"
my_dict["r1"] = "tag 1"
my_dict["r2"] = "tag 2"
...
Note that both the key and value follows a pattern, i.e., the number increase by 1. Now I cannot do this 1M times and would prefer an automatic way to initialize my dictionary.
The most efficient way to do this is probably with a dict comprehension:
mydict={'r%s'%n : 'tag %s'%n for n in range(10)}
Which is equivalent to:
mydict=dict()
for n in range(10):
mydict.update({'r%s'%n:'tag %s'%n})
... but more efficient. Just change range(10) as necessary.
You could also use .format() formatting instead of percent (C-like) formatting in the dict:
mydict={'r{}'.format(n) : 'tag {}'.format(n) for n in range(10)}
If you are using Python2 replace all the range() functions with xrange() functions
my_dict = dict()
for i in range(0, 1000000):
key = "r{}".format(i)
value = "tag {}".format(i)
my_dict[key] = value
EDIT: As pointed out by others, if you are using python 2 use xrange instead since it is lazy (so more efficient). In Python 3 range does the same thing as xrange in python 2
my_dict = dict()
for i in xrange(1000000):
my_dict["r%s" % i] = "tag %s" % i
my_dict = dict()
for x in range(1000000):
key="r"+str(x)
val="tag " +str(x)
my_dict[key]=val
simple way is to do the following
#using python format strings
keyf = "r{}"
valf = "tag {}"
#dictionary comprehension
a = {keyf.format(i) : valf.format(i) for i in range(5)}
# can modify range to handle 1,000,000 if you wanted
print(a)
{'r0': 'tag 0', 'r1': 'tag 1', 'r2': 'tag 2', 'r3': 'tag 3', 'r4': 'tag 4', 'r5': 'tag 5'}
if you wanted to quickly append this to another dictionary you would use the dictionary equivalent of extend, which is called update.
b = dict{"x":1,"y":2}
b.update(a)
print(b)
{'x': 1, 'y': 2, 'r0': 'tag 0', 'r1': 'tag 1', 'r2': 'tag 2', 'r3': 'tag 3', 'r4': 'tag 4'}
you could also shorten the original comprehension by doing this:
a = {"r{}".format(i) : "tag {}".format(i) for i in range(5)}
You wouldn't even need to make keyf, or valf
Python can build dicts from lists:
$ python2 -c "print dict(map(lambda x: ('r' + str(x), 'tag ' + str(x)), range(10)))"
{'r4': 'tag 4', 'r5': 'tag 5', 'r6': 'tag 6', 'r7': 'tag 7', 'r0': 'tag 0', 'r1': 'tag 1', 'r2': 'tag 2', 'r3': 'tag 3', 'r8': 'tag 8', 'r9': 'tag 9'}