I have a python list as follows:
>>>list1 = ['Mary','had','a','little','lamb','which','was','very','naughty']
Now i want to create a create a sublist with elements from little to very. So i did the following :
>>> list1[list1.index('little'):list1.index('very')+1]
['little', 'lamb', 'which', 'was', 'very']
What I want to do is rather than specifying the entire element, check for a substring in the element using the in function and then slice the list like :
>>> list1[list1.index('lit'):list1.index('ver')+1]
how would i do that ?
As i told in my preceding answer you don't need to use list.index method, you can use the following function that is faster and also you can apply any calculation on the elements :
def drop(iterable,start,end):
for i,x in enumerate(iterable):
if x==start :
for y in iterable[i:]:
if y!=end and 'sub_string' in y: #or 'sub_string' not in y
yield y
else:
#based on your problem do stuff
#yield end
break
Enumerate is your friend. With that and list comprehensions it becomes pretty easy.
list1[[i for i, j in enumerate(list1) if "lit" in j][0]:[i for i, j in enumerate(list1) if "ver" in j][0]+1]
What this code does is it takes the elements only if it contains the string, but stores the index not the element. Doing this for start and end, and using the first occurrence gives the desired result.
To maintain readability, you will want to break it up into multiple statements rather than use a large list comprehension:
>>> list1 = ['Mary','had','a','little','lamb','which','was','very','naughty']
>>> first = next(i for i, v in enumerate(list1) if 'lit' in v)
>>> last = next(i for i, v in enumerate(list1) if 'ver' in v)
>>> list1[first:last+1]
['little', 'lamb', 'which', 'was', 'very']
This is probably not an answer you are looking for (especially taking number of lines). But I want to show different approach (especially if you are new to Python):
class fuzzyFilter:
def __init__(startSubstr, endSubstr):
self.start = startSubstr
self.end = endSubstr
self.state = None
def __call__(value):
if self.state is None:
if self.start in value:
self.state = True
return True
elif self.state:
if self.end in value:
self.state = False
return True
return False
print filter(fuzzyFilter('lit', 'very'), list1)
There are a couple of problems here:
fuzzyFilter could be used only once (or some sort of reset is required)
It is guaranteed, that whole list1 would be read (not always true in other answers)
Related
I am trying to code this,
def retrieve_smallest_letter(words):
"""
Input: 'words' (lst) which represents a list of strings.
Output: A new list with the smaller letter of the each word's
first and last index value compared to be appended to the list.
For Example:
>>> lst = ['sandbox', 'portabello', 'lion', 'australia', 'salamander']
>>> retrieve_smallest_letter(lst)
['s', 'o', 'l', 'a', 'r']
"""
My code
def retrieve_smallest_letter(words):
lst = []
for i in range(len(words)):
first_word = words[i][0]
last_word = words[i][len(words[i])-1]
if first_word < last_word:
lst.append(str(first_word))
else:
lst.append(str(last_word))
return lst
How can I shorten this code with list comprehension?
First thing to understand is that a list comprehension is fundamentally restricted semantics on a for loop:
r = [a for a in b if c]
is essentially syntactic sugar for
r = []
for a in b:
if c:
r.append(a)
so the first step is to get the problem into a "shape" which fits a list comprehension:
simple iteration and filtering
no assignments (because that's as yet not supported)
only one production
Using Python correctly also help, do let's start by simplifying the existing loop:
iterate collections directly, Python has a powerful iterator protocol and you don't usually iterate by indexing unless absolutely necessary
use indexing or destructuring on the word, Python allows indexing from the end (using negative indices)
perform the selection "inline" as a single expression, using either a conditional expression or in this case the min builtin which does exactly what's needed
def retrieve_smallest_letter(words):
lst = []
for word in words:
lst.append(min(word[0], word[-1]))
return lst
or
def retrieve_smallest_letter(words):
lst = []
for first, *_, last in words:
lst.append(min(first, last))
return lst
from there, the conversion is trivial (there is no filtering so it can be ignored):
def retrieve_smallest_letter(words):
return [min(first, last) for first, *_, last in words]
Yes
[min(word[0], word[-1]) for word in words]
lst = [min(w[0], w[-1]) for w in words]
I would like to find the first time an element occurs for the second time in a list. For example, if my list was
['1','2','1B','2B','2B','2','1B','1']
the result should be '2B' (or it could return the index 4), since the element '2B' is the first element to occur twice (going left to right).
I know I can do this with a basic for loop counting occurrences as I go along; I just wondered what's the most efficient way to do it.
You can't do better than worst case O(N). If you want be concise and don't mind some side-effect kung-fu, you can use next with a conditional generator:
lst = ['1','2','1B','2B','2B','2','1B','1']
s = set()
next(x for x in lst if x in s or s.add(x))
# '2B'
You could loop over the elements in the list, keep track of which have appeared by adding them to a set, and break as soon as an element is already in the set:
l = ['1','2','1B','2B','2B','2','1B','1']
s = set()
for i in l:
if i in s:
result = i
break
else:
s.add(i)
print(result)
# '2B'
You could do:
appearance = set()
data = ['1','2','1B','2B','2B','2','1B','1']
for i, d in enumerate(data):
if d in appearance:
print(i)
break
else:
appearance.add(d)
Output
4
x = ['1','2','1B','2B','2B','2','1B','1']
d = {}
for i in x:
if not i in d:
d[i] = 0
else:
print(i)
break
output
2B
Another solution but not as efficient as yatu's answer.
l = ['1','2','1B','2B','2B','2','1B','1']
next(x for i, x in enumerate(l, 1) if len(l[:i]) > len(set(l[:i])))
# '2B'
In addition to the great answers posted, it's worth mentioning itertools.takewhile:
>>> from itertools import takewhile
>>> ls = ['1','2','1B','2B','2B','2','1B','1']
>>> seen = set()
>>> len(list(takewhile(lambda x: x not in seen and not seen.add(x), ls)))
4
or
>>> list(takewhile(lambda x: x not in seen and not seen.add(x), ls)).pop()
'2B'
The above raises IndexError if the list is empty and both methods return the whole list if all items are unique, requiring a bit of interpretation.
This also generates a temporary list (unlike the explicit loop approach) and is not especially readable, but at least only performs a partial traversal when a dupe does exist and makes it easy to get the index or the group of elements to the left of the duplicate.
I would like a way to find which element in a list is not ""
For example, for this:
['','','b']
It should return 2 because the index of "b" is 2
The following uses enumerate to tie each element to a numeric index, then we filter the array for non-empty strings.
If you want all instances:
test = ['','','b']
print(filter(lambda a: a[1] != "", enumerate(test)))
Returns
[(2, 'b')]
If you only want the first instance:
test = ['','','b']
print(filter(lambda a: a[1] != "", enumerate(test))[0][0])
If you want a list of all instances:
test = ['','','b']
print([ele[0] for ele in filter(lambda a: a[1] != "", enumerate(test))])
A nice, fast way can use next() with enumerate() and a generator expression:
next(index for index,item in enumerate(mylist) if item != '')
That is very efficient; it stops checking as soon as it finds a match. If everything in the list is '', it will throw a StopIteration exception. If you want to have a default value, say -1:
next((index for index,item in enumerate(mylist) if item != ''), -1)
You can use a simple for loop and check if the value is not '' then save the index of the value within a list comprehension to solve your problem
test = ['','','b']
print([test.index(x) for x in test if x != ''][0]) # output 2
All the other answers looks concise , but it will iterate over the entire list even if the first element is not ''. If you just need the first element which is not '', I think you should use a simple for loop.
def function(a,k):
for i, j in enumerate(a):
if j != k:
return i
a = ['','','b']
k=''
print(function(a,k))
Basically, I'm trying to flatten a list in my function but ignore that (you can also ignore the print functions I put in).
take x = [[1,2,3],4,5] to be my variable.
I call prob7(x) but the issue is that when type([1,2,3]) gets checked == list, it returns false. Why is that? I explicitly check this on the interpreter command line and it returns true. But inside the function, I get a false.
Just a bug that I missed because I'm sleepy or am I misunderstanding some part of the Python language? I run version 2.6 if it matters.
def prob7(list): # flatten a list
tempList = []
if list: # meaning if there are elements in the list and it is not empty
for i in list:
if type(i) != list:
print tempList,'if',i,type(i)==list
tempList.append(i)
else:
print tempList,'else',i
tempList.extend(prob7(i))
return tempList
Just not use 'list' as a variable name and use isinstance(var, list) instead of type(var) == list.
Please find corrected sample below.
def prob7(mylist): # flatten a list
tempList = []
if mylist: # meaning if there are elements in the list and it is not empty
for i in mylist:
if not isinstance(i, list):
print tempList, 'if', i, isinstance(i, list)
tempList.append(i)
else:
print tempList, 'else', i
tempList.extend(prob7(i))
return tempList
Or if you don't really required to use recursion and you don't care about values order then you can use something like this:
lVals = [[1,2,3],4,5, [1,[4,7]]]
def make_flat(mylist): # flatten a list
while any(isinstance(x, list) for x in mylist):
for i, val in enumerate(mylist):
if isinstance(val, list):
mylist.extend(mylist.pop(i))
break
return mylist
make_flat(lVals)
>>> [4, 5, 1, 2, 3, 1, 4, 7]
Artisom has your answer. In addtion, type checks are not very Pythonic. Duck typing often is the way to go. In case your elements are numbers only, the following does the job too, without explicit type checks but behavior checks:
def prob7(inlist): # flatten a list
outlist = []
for x in inlist:
try:
outlist += x
except TypeError:
outlist.append(x)
return outlist
Note that string elements in this implementation would behave like nested lists. Anyway, just wanted to illustrate what it means to expect behavior, not types.
Some alternate approaches:
# Iterative, but more functional-style
def flatten(a_list):
while any(isinstance(x, list) for x in a_list):
a_list = sum((x if isinstance(x, list) else [x] for x in a_list), [])
return a_list
# Using a generator recursively,
# then evaluating the generator to produce the list
# instead of explicitly appending each element.
def flatten_gen(a_list):
for x in a_list:
if isinstance(x, list):
for y in flatten_gen(x): yield y
else: yield x
def flatten(a_list): return list(flatten_gen(a_list))
The problem here is you are using a local variable name (list) that is the same as the global list type. You should change your variable name. Also, when checking types like that you can use the is operator.
type(l) is list
But here's my version of flatten.
def flatten(alist):
rv = []
for val in alist:
if isinstance(val, list):
rv.extend(flatten(val))
else:
rv.append(val)
return rv
This does not alter the original list, but returns a new list. This is consistent with most other patterns.
This question already has answers here:
Finding the index of an item in a list
(43 answers)
Closed 9 years ago.
What is a good way to find the index of an element in a list in Python?
Note that the list may not be sorted.
Is there a way to specify what comparison operator to use?
From Dive Into Python:
>>> li
['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements']
>>> li.index("example")
5
If you just want to find out if an element is contained in the list or not:
>>> li
['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements']
>>> 'example' in li
True
>>> 'damn' in li
False
The best way is probably to use the list method .index.
For the objects in the list, you can do something like:
def __eq__(self, other):
return self.Value == other.Value
with any special processing you need.
You can also use a for/in statement with enumerate(arr)
Example of finding the index of an item that has value > 100.
for index, item in enumerate(arr):
if item > 100:
return index, item
Source
Here is another way using list comprehension (some people might find it debatable). It is very approachable for simple tests, e.g. comparisons on object attributes (which I need a lot):
el = [x for x in mylist if x.attr == "foo"][0]
Of course this assumes the existence (and, actually, uniqueness) of a suitable element in the list.
assuming you want to find a value in a numpy array,
I guess something like this might work:
Numpy.where(arr=="value")[0]
There is the index method, i = array.index(value), but I don't think you can specify a custom comparison operator. It wouldn't be hard to write your own function to do so, though:
def custom_index(array, compare_function):
for i, v in enumerate(array):
if compare_function(v):
return i
I use function for returning index for the matching element (Python 2.6):
def index(l, f):
return next((i for i in xrange(len(l)) if f(l[i])), None)
Then use it via lambda function for retrieving needed element by any required equation e.g. by using element name.
element = mylist[index(mylist, lambda item: item["name"] == "my name")]
If i need to use it in several places in my code i just define specific find function e.g. for finding element by name:
def find_name(l, name):
return l[index(l, lambda item: item["name"] == name)]
And then it is quite easy and readable:
element = find_name(mylist,"my name")
The index method of a list will do this for you. If you want to guarantee order, sort the list first using sorted(). Sorted accepts a cmp or key parameter to dictate how the sorting will happen:
a = [5, 4, 3]
print sorted(a).index(5)
Or:
a = ['one', 'aardvark', 'a']
print sorted(a, key=len).index('a')
how's this one?
def global_index(lst, test):
return ( pair[0] for pair in zip(range(len(lst)), lst) if test(pair[1]) )
Usage:
>>> global_index([1, 2, 3, 4, 5, 6], lambda x: x>3)
<generator object <genexpr> at ...>
>>> list(_)
[3, 4, 5]
I found this by adapting some tutos. Thanks to google, and to all of you ;)
def findall(L, test):
i=0
indices = []
while(True):
try:
# next value in list passing the test
nextvalue = filter(test, L[i:])[0]
# add index of this value in the index list,
# by searching the value in L[i:]
indices.append(L.index(nextvalue, i))
# iterate i, that is the next index from where to search
i=indices[-1]+1
#when there is no further "good value", filter returns [],
# hence there is an out of range exeption
except IndexError:
return indices
A very simple use:
a = [0,0,2,1]
ind = findall(a, lambda x:x>0))
[2, 3]
P.S. scuse my english