Sorry for the very basic question, but this is actually a 2-part question:
Given a list, I need to replace the values of '?' with 'i' and the 'x' with an integer, 10. The list does not always have the same number of elements, so I need a loop that permits me to do this.
a = ['1', '7', '?', '8', '5', 'x']
How do I grab the index of where the value is equal to '?'. It'd be nice if this show me how I could grab all the index and values in a list as well.
Write a function for it and use map() to call it on every element:
def _replaceitem(x):
if x == '?':
return 'i'
elif x == 'x':
return 10
else:
return x
a = map(_replaceitem, a)
Note that this creates a new list. If the list is too big or you don't want this for some other reason, you can use for i in xrange(len(a)): and then update a[i] if necessary.
To get (index, value) pairs from a list, use enumerate(a) which returns an iterator yielding such pairs.
To get the first index where the list contains a given value, use a.index('?').
For 1:
for i in range(len(a)):
if a[i] == '?':
a[i] = 'i'
elif a[i] == 'x':
a[i] = 10
For 2, what do you mean by "key"? If you mean index:
index = a.index('?')
Only because no one's mentioned it yet, here's my favourite non-for-loop idiom for performing replacements like this:
>>> a = ['1', '7', '?', '8', '5', 'x']
>>> reps = {'?': 'i', 'x': 10}
>>> b = [reps.get(x,x) for x in a]
>>> b
['1', '7', 'i', '8', '5', 10]
The .get() method is incredibly useful, and scales up better than an if/elif chain.
Start by reading the Built-in Types section of the Library Reference. I think that you are looking for list.index.
it is function called 'index':
>>> a = ['1', '7', '?', '8', '5', 'x']
>>> a.index('?')
2
You can now use lambda
# replace occurrences of ?
a = map(lambda x: i if x == '?' else x, a)
# replace occurrences of x
a = list(map(lambda x: 10 if x == 'x' else x, a))
a = ['1', '7', '?', '8', '5', 'x']
for index, item in enumerate(a):
if item == "?":
a[index] = "i"
elif item == "x":
a[index = 10
print a
Related
I have an assignment for which my script should be able to receive a string for input (e.g. "c27bdj3jddj45g" ) and extract the numbers into a list (not just the digits, it should be able to detect full numbers).
I am not allowed to use regex at all, only simple methods like split, count and append.
Any ideas? (Using python)
Example for the output needed for the string I gave as an example:
['27','3', '45']
Nothing I have tried so far is worth mentioning here, I am pretty lost on which approach to take here without re.findall, which I cannot use.
One way to solve it is to use the groupby from itertools lib:
from itertools import groupby
s = 'c27bdj3jdj45g11' # last dight is 11
ans = []
for k, g in groupby(s, lambda x: x.isdigit()):
if k: # True if x is digit
ans.append(''.join(g))
ans
['27', '3', '45', '11']
Second solution - even OP has opt out the regex, but this is just for a reference. (to show how much easier to approach this type of puzzle - which should be the way to go)
You could try to use regex - re lib like this (if there's no restriction!)
s = 'c27bdj3jddj45g'
import re
list(re.findall(r'\d+', s)) # matching one more digits
['27', '3', '45']
# or to get *integer*
list(map(int, re.findall(r'\d+', s)))
[27, 3, 45]
You can do this with a for-loop and save the numbers. Then, when you see no digit, append digits and reset the string.
s = 'g38ff11'
prv = ''
res = []
for c in s:
if c.isdigit():
prv += c
else:
if prv != '': res.append(prv)
prv = ''
if prv != '': res.append(prv)
print(res)
Output:
['38', '11']
You can also write a lambda to check and append:
s = 'g38ff11'
prv = ''
res = []
append_dgt = lambda prv, res: res.append(prv) if prv!="" else None
for c in s:
if c.isdigit():
prv += c
else:
append_dgt(prv, res)
prv = ''
append_dgt(prv, res)
print(res)
s='c27bdj3jddj45g'
lst=[]
for x in s:
if x.isdigit():
lst.append(x)
else:
lst.append('$') # here $ is appended as a place holder so that all the numbers can come togetrher
Now, lst becomes :
#['$', '2', '7', '$', '$', '$', '3', '$', '$', '$', '$', '4', '5', '$']
''.join(lst).split('$') becomes:
['', '27', '', '', '3', '', '', '', '45', '']
Finally doing list comprehension to extract the numbers:
[x for x in ''.join(lst).split('$') if x.isdigit()]
['27', '3', '45']
string='c27bdj3jddj45g'
lst=[]
for i in string:
if i.isdigit():
lst.append(i)
else:
lst.append('$')
print([int(i) for i in ''.join(lst).split('$') if i.isdigit()])
I want to make a function that takes in a student's test answers and an answer key and return a list that states if they got the answer right or wrong using 1's and 0's.
Here is my code
answerkey='ABCABCDDD'
student11='BBCCBCDDD'
def check_answers(X='student',Y='answer key'):
result=[]
for i in range(len(X)):
for o in range(len(Y)):
if i==o:
result.append('1')
else:
result.append('0')
return result
print(check_answers(student11,answerkey))
My output should give me:
['0','1','1','0','1','1','1','1','1']
instead it gives me this:
['1', '0', '0', '0', '0', '0', '0', '0', '0']
What am i doing wrong?
This can all be simplified by using a comprehension and zip:
>>> ['1' if x == y else '0' for x, y in zip(answerkey, student11)]
['0', '1', '1', '0', '1', '1', '1', '1', '1']
However, to address your problem, you actually are doing an unnecessary extra step with that inner loop. It is an extra inefficiency that you don't need. Rely on the fact that your two lists are equal, so iterate over one, and just compare against the other list. Use enumerate, so that in each iteration you have access to the index and value.
Also, you are not using your default keyword arguments properly. You are assigning them as a string by default and not really doing anything with a default case for that kind of assignment, so remove it.
Observe:
answerkey='ABCABCDDD'
student11='BBCCBCDDD'
def check_answers(X, Y):
result=[]
for i, v in enumerate(answerkey):
if v == student11[i]:
result.append('1')
else:
result.append('0')
return result
print(check_answers(student11,answerkey))
Output:
['0', '1', '1', '0', '1', '1', '1', '1', '1']
This is happening because you o and i are counting through a range i.e. they are counting 0,1,2...len(X) and you are also iterating through every value of Y for each value of X instead you should compare variables in the list of the same index
answerkey='ABCABCDDD'
student11='BBCCBCDDD'
def check_answers(X,Y):
result=[]
for i in range(len(X)):
if X[i] == Y[i]:
result.append('1')
else:
result.append('0')
return result
print(check_answers(student11,answerkey))
In this algorithm it iterates through the length of X and compares each vallue of the list with the same indices e.g. it checks if X[0] = Y[0]
Also in your function you make X='student' and Y='answer key' this is fine because you only have two parameters and it overrides them but if you were to add more later this could cause a problem, if your intention was to just have these as comments I suggest just putting a #X = student and Y = answer key underneath it is cleaner
If you want to figure out what you're doing wrong, I've strategically put in 2 print statements that will show you what the issue is with your code.
def check_answers(X='student',Y='answer key'):
result=[]
print('X: %d, Y: %d' % (len(X), len(Y)))
for i in range(len(X)):
for o in range(len(Y)):
print('i: %d, o: %d' % (i, o))
if i==o:
result.append('1')
else:
result.append('0')
return result
Just in addition for other answers I'd recommend you use map function
answerkey='ABCABCDDD'
student11='BBCCBCDDD'
def check_answers(X='student',Y='answer key'):
return map(lambda a,b:a==b and '1' or '0',X,Y)
print(check_answers(answerkey,student11))
This is my code.
lst=['0','1','2','3','4']
i = lst.index('2')
lst.pop(i)
tmp=[]
tmp.append('2')
tmp.extend(lst)
lst = tmp
print lst #output:['2','0','1','3','4']
Now I want to write pretty code. I think it may be to have room for improvement.So, I hope anyone who can explain and instruct me.Thanks!
sorted([0,1,2,3,4,5], key=lambda x: x == 2, reverse=True)
As an alternative answer you can use slicing :
>>> i = lst.index('2')
>>> ['2']+lst[:i]+lst[i+1:]
['2', '0', '1', '3', '4']
You can embed it inside a function :
>>> def move(elem,l):
... i = l.index(elem)
... return [elem]+lst[:i]+lst[i+1:]
...
>>> move('2',lst)
['2', '0', '1', '3', '4']
Donig it in place (altering the list):
lst = lst.pop(lst.index(2)) and (not lst.insert(0, 2)) and lst
Creating a new list for the result:
[2] + (lst.pop(lst.index(2)) and lst)
If i have a few lists
I am able to combine list 1 with list 2 however, I have not managed to succeed to combine the other lists.
def alternator():
iets = []
for i in range(len(list2)):
something += [list1[i]]
something +=[list2[i]]
result = something
result_weaver(result)
def result(x):
list31 = list3
if len(list3) < len(x) :
while len(list31) != len(x):
list31 += '-'
I decided to add '-' in order to make sure the lengths of both lists were equal, so the for loop could go to work.
Does anyone have better ideas on how to program this ?
Use itertools.zip_longest() here:
try:
from itertools import zip_longest
except ImportError:
# Python 2
from itertools import izip_longest as zip_longest
def alternate(list1, list2):
return [v for v in sum(zip_longest(list1, list2), ()) if v is not None]
The zip_longest() call adds None placeholders (similar to your own attempt to add - characters), which we need to remove again from the sum() output after zipping.
Demo:
>>> alternate(list1, list2)
['1', '5', '2', '6', '3', '7', '8']
>>> alternate(alternate(list1, list2), list3)
['1', '9', '5', '2', '6', '3', '7', '8']
I very often run into the need to split a sequence into the two subsequences of elements that satisfy and don't satisfy a given predicate (preserving the original relative ordering).
This hypothetical "splitter" function would look something like this in action:
>>> data = map(str, range(14))
>>> pred = lambda i: int(i) % 3 == 2
>>> splitter(data, pred)
[('2', '5', '8', '11'), ('0', '1', '3', '4', '6', '7', '9', '10', '12', '13')]
My question is:
does Python already have a standard/built-in way to do this?
This functionality is certainly not difficult to code (see Addendum below), but for a number of reasons, I'd prefer to use a standard/built-in method than a self-rolled one.
Thanks!
Addendum:
The best standard function I've found so far for handling this task in Python is itertools.groupby. To use it for this particular task however, it is necessary to call the predicate function twice for each list member, which I find annoyingly silly:
>>> import itertools as it
>>> [tuple(v[1]) for v in it.groupby(sorted(data, key=pred), key=pred)]
[('0', '1', '3', '4', '6', '7', '9', '10', '12', '13'), ('2', '5', '8', '11')]
(The last output above differs from the desired one shown earlier in that the subsequence of elements that satisfy the predicate comes last rather than first, but this is very minor, and very easy to fix if needed.)
One can avoid the redundant calls to the predicate (by doing, basically, an "inline memoization"), but my best stab at this gets pretty elaborate, a far cry from the simplicity of splitter(data, pred):
>>> first = lambda t: t[0]
>>> [zip(*i[1])[1] for i in it.groupby(sorted(((pred(x), x) for x in data),
... key=first), key=first)]
[('0', '1', '3', '4', '6', '7', '9', '10', '12', '13'), ('2', '5', '8', '11')]
BTW, if you don't care about preserving the original ordering, sorted's default sort order gets the job done (so the key parameter may be omitted from the sorted call):
>>> [zip(*i[1])[1] for i in it.groupby(sorted(((pred(x), x) for x in data)),
... key=first)]
[('0', '1', '3', '4', '6', '7', '9', '10', '12', '13'), ('2', '5', '8', '11')]
I know you said you didn't want to write your own function, but I can't imagine why. Your solutions involve writing your own code, you just aren't modularizing them into functions.
This does exactly what you want, is understandable, and only evaluates the predicate once per element:
def splitter(data, pred):
yes, no = [], []
for d in data:
if pred(d):
yes.append(d)
else:
no.append(d)
return [yes, no]
If you want it to be more compact (for some reason):
def splitter(data, pred):
yes, no = [], []
for d in data:
(yes if pred(d) else no).append(d)
return [yes, no]
Partitioning is one of those itertools recipes that does just that. It uses tee() to make sure it's iterating the collection in one pass despite the multiple iterators, the builtin filter() function to grab items that satisfies the predicate as well as filterfalse() to get the opposite effect of the filter. This is as close as you're going to get at a standard/builtin method.
def partition(pred, iterable):
'Use a predicate to partition entries into false entries and true entries'
# partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9
t1, t2 = tee(iterable)
return filterfalse(pred, t1), filter(pred, t2)
In more_itertools there is a function called partition, which does exactly what topicstarter asked for.
from more_itertools import partition
numbers = [1, 2, 3, 4, 5, 6, 7]
predicate = lambda x: x % 2 == 0
predicate_false, predicate_true = partition(predicate, numbers)
print(list(predicate_false), list(predicate_true))
The result is [1, 3, 5, 7] [2, 4, 6].
If you don't care about efficiency, I think groupby (or any "putting data into n bins" functions) has some nice correspondence,
by_bins_iter = itertools.groupby(sorted(data, key=pred), key=pred)
by_bins = dict((k, tuple(v)) for k, v in by_bins_iter)
You can then get to your solution by,
return by_bins.get(True, ()), by_bins.get(False, ())
A slight variation of one of the OP's implementations and another commenter's implementation above using groupby:
groups = defaultdict(list, { k : list(ks) for k, ks in groupby(items, f) })
groups[True] == the matching items, or [] if none returned True
groups[False] == the non-matching items, or [] if none returned False
Sadly, as you point out, groupby requires that the items be sorted by the predicate first, so if that's not guaranteed, you need this:
groups = defaultdict(list, { k : list(ks) for k, ks in groupby(sorted(items, key=f), f) })
Quite a mouthful, but it is a single expression that partitions a list by a predicate using only built-in functions.
I don't think you can just use sorted without the key parameter, because groupby creates a new group when it hits a new value from the key function. So sorted will only work if the items sort naturally by the predicate provided.
As a slightly more general solution to partitioning, consider grouping. Consider the following function, inspired by clojure's group-by function.
You give it a collection of items to group, and a function that will be used to group them. Here's the code:
def group_by(seq, f):
groupings = {}
for item in seq:
res = f(item)
if res in groupings:
groupings[res].append(item)
else:
groupings[res] = [item]
return groupings
For the OP's original case:
y = group_by(range(14), lambda i: int(i) % 3 == 2)
{False: [0, 1, 3, 4, 6, 7, 9, 10, 12, 13], True: [2, 5, 8, 11]}
A more general case of grouping elements in a sequence by string length:
x = group_by(["x","xx","yy","zzz","z","7654321"], len)
{1: ['x', 'z'], 2: ['xx', 'yy'], 3: ['zzz'], 7: ['7654321']}
This can be extended to many cases, and is a core functionality of functional languages. It works great with the dynamically typed python, as the keys in the resulting map can be any type. Enjoy!
Reducing the iterable into the 2 partitions using functools.reduce you can get rid of the key function:
import functools
functools.reduce(
lambda tf, x: (tf[0], [*tf[1], x]) if pred(x) else ([*tf[0], x], tf[1]),
data,
([], []),
)
>>> (['0', '1', '3', '4', '6', '7', '9', '10', '12', '13'], ['2', '5', '8', '11'])