Python slice first and last element in list - python

Is there a way to slice only the first and last item in a list?
For example; If this is my list:
>>> some_list
['1', 'B', '3', 'D', '5', 'F']
I want to do this (obviously [0,-1] is not valid syntax):
>>> first_item, last_item = some_list[0,-1]
>>> print first_item
'1'
>>> print last_item
'F'
Some things I have tried:
In [3]: some_list[::-1]
Out[3]: ['F', '5', 'D', '3', 'B', '1']
In [4]: some_list[-1:1:-1]
Out[4]: ['F', '5', 'D', '3']
In [5]: some_list[0:-1:-1]
Out[5]: []
...

One way:
some_list[::len(some_list)-1]
A better way (Doesn't use slicing, but is easier to read):
[some_list[0], some_list[-1]]

Python 3 only answer (that doesn't use slicing or throw away the rest of the list, but might be good enough anyway) is use unpacking generalizations to get first and last separate from the middle:
first, *_, last = some_list
The choice of _ as the catchall for the "rest" of the arguments is arbitrary; they'll be stored in the name _ which is often used as a stand-in for "stuff I don't care about".
Unlike many other solutions, this one will ensure there are at least two elements in the sequence; if there is only one (so first and last would be identical), it will raise an exception (ValueError).

Just thought I'd show how to do this with numpy's fancy indexing:
>>> import numpy
>>> some_list = ['1', 'B', '3', 'D', '5', 'F']
>>> numpy.array(some_list)[[0,-1]]
array(['1', 'F'],
dtype='|S1')
Note that it also supports arbitrary index locations, which the [::len(some_list)-1] method would not work for:
>>> numpy.array(some_list)[[0,2,-1]]
array(['1', '3', 'F'],
dtype='|S1')
As DSM points out, you can do something similar with itemgetter:
>>> import operator
>>> operator.itemgetter(0, 2, -1)(some_list)
('1', '3', 'F')

first, last = some_list[0], some_list[-1]

Some people are answering the wrong question, it seems. You said you want to do:
>>> first_item, last_item = some_list[0,-1]
>>> print first_item
'1'
>>> print last_item
'F'
Ie., you want to extract the first and last elements each into separate variables.
In this case, the answers by Matthew Adams, pemistahl, and katrielalex are valid. This is just a compound assignment:
first_item, last_item = some_list[0], some_list[-1]
But later you state a complication: "I am splitting it in the same line, and that would have to spend time splitting it twice:"
x, y = a.split("-")[0], a.split("-")[-1]
So in order to avoid two split() calls, you must only operate on the list which results from splitting once.
In this case, attempting to do too much in one line is a detriment to clarity and simplicity. Use a variable to hold the split result:
lst = a.split("-")
first_item, last_item = lst[0], lst[-1]
Other responses answered the question of "how to get a new list, consisting of the first and last elements of a list?" They were probably inspired by your title, which mentions slicing, which you actually don't want, according to a careful reading of your question.
AFAIK are 3 ways to get a new list with the 0th and last elements of a list:
>>> s = 'Python ver. 3.4'
>>> a = s.split()
>>> a
['Python', 'ver.', '3.4']
>>> [ a[0], a[-1] ] # mentioned above
['Python', '3.4']
>>> a[::len(a)-1] # also mentioned above
['Python', '3.4']
>>> [ a[e] for e in (0,-1) ] # list comprehension, nobody mentioned?
['Python', '3.4']
# Or, if you insist on doing it in one line:
>>> [ s.split()[e] for e in (0,-1) ]
['Python', '3.4']
The advantage of the list comprehension approach, is that the set of indices in the tuple can be arbitrary and programmatically generated.

What about this?
>>> first_element, last_element = some_list[0], some_list[-1]

You can do it like this:
some_list[0::len(some_list)-1]

You can use something like
y[::max(1, len(y)-1)]
if you really want to use slicing. The advantage of this is that it cannot give index errors and works with length 1 or 0 lists as well.

Actually, I just figured it out:
In [20]: some_list[::len(some_list) - 1]
Out[20]: ['1', 'F']

This isn't a "slice", but it is a general solution that doesn't use explicit indexing, and works for the scenario where the sequence in question is anonymous (so you can create and "slice" on the same line, without creating twice and indexing twice): operator.itemgetter
import operator
# Done once and reused
first_and_last = operator.itemgetter(0, -1)
...
first, last = first_and_last(some_list)
You could just inline it as (after from operator import itemgetter for brevity at time of use):
first, last = itemgetter(0, -1)(some_list)
but if you'll be reusing the getter a lot, you can save the work of recreating it (and give it a useful, self-documenting name) by creating it once ahead of time.
Thus, for your specific use case, you can replace:
x, y = a.split("-")[0], a.split("-")[-1]
with:
x, y = itemgetter(0, -1)(a.split("-"))
and split only once without storing the complete list in a persistent name for len checking or double-indexing or the like.
Note that itemgetter for multiple items returns a tuple, not a list, so if you're not just unpacking it to specific names, and need a true list, you'd have to wrap the call in the list constructor.

How about this?
some_list[:1] + some_list[-1:]
Result: ['1', 'F']

More General Case: Return N points from each end of list
The answers work for the specific first and last, but some, like myself, may be looking for a solution that can be applied to a more general case in which you can return the top N points from either side of the list (say you have a sorted list and only want the 5 highest or lowest), i came up with the following solution:
In [1]
def GetWings(inlist,winglen):
if len(inlist)<=winglen*2:
outlist=inlist
else:
outlist=list(inlist[:winglen])
outlist.extend(list(inlist[-winglen:]))
return outlist
and an example to return bottom and top 3 numbers from list 1-10:
In [2]
GetWings([1,2,3,4,5,6,7,8,9,10],3)
#Out[2]
#[1, 2, 3, 8, 9, 10]

Fun new approach to "one-lining" the case of an anonymously split thing such that you don't split it twice, but do all the work in one line is using the walrus operator, :=, to perform assignment as an expression, allowing both:
first, last = (split_str := a.split("-"))[0], split_str[-1]
and:
first, last = (split_str := a.split("-"))[::len(split_str)-1]
Mind you, in both cases it's essentially exactly equivalent to doing on one line:
split_str = a.split("-")
then following up with one of:
first, last = split_str[0], split_str[-1]
first, last = split_str[::len(split_str)-1]
including the fact that split_str persists beyond the line it was used and accessed on. It's just technically meeting the requirements of one-lining, while being fairly ugly. I'd never recommend it over unpacking or itemgetter solutions, even if one-lining was mandatory (ruling out the non-walrus versions that explicitly index or slice a named variable and must refer to said named variable twice).

Related

How do I access the 1st element of each list in a lists of lists

Example:
numbers = ['1','2','3']
letters = ['a','b','c']
I want to get [1,a] as a results. Yeah I can loop through it, but I'm wondering if there is a fast one line way of doing this.
EDIT EDIT !!!!
I made a horrible mistake in describing the problem.
I have access to the combined list (the list of lists of the question):
list_of_lists = [ numbers, letters]
which is equal to:
[ ['1','2','3'],['a','b','c']]
Sorry for the confusion. The end result is still the same, this would be ['1','a'].
Try a list comprehension:
# (numbers, letters) can be replaced with `list_of_lists`
>>> [ x[0] for x in (numbers, letters) ]
['1', 'a']
import operator
map(operator.itemgetter(0), [numbers, letters])
list_of_lists = [['1', '2', '3'], ['a', 'b', 'c']]
list_of_firsts = [l[0] for l in list_of_lists]
You may be looking for zip
I would try:
zip(*list_of_lists)[0]

How can I get a reversed copy of a list (avoid a separate statement when chaining a method after .reverse)?

This code fails:
fCamel = 'F'
bCamel = 'B'
gap = ' '
k = ['F', ' ', 'B', 'F']
def solution(formation):
return ((formation.index(bCamel) > (len(formation) - 1 - (formation.reverse()).index(fCamel))))
solution(k)
I get an exception that says AttributeError: 'NoneType' object has no attribute 'index'.
I know that the problem is that list.reverse() returns None, modifying the list in-place. I want to use .index on the reversed list. Is there a way I can avoid using a separate statement to reverse the list before indexing into it? How?
You can use slicing to return the reversed list:
l[::-1]
You can use reversed(formation) to return a reverse iterator of formation. When you call formation.reverse() it does an in place reversal of the list and returns None.
EDIT:
I see what you are trying to do now, in my opinion it's easier to just do this with a list comprehension:
def solution(formation):
return len([k for k in formation[formation.index(bCamel)+1:] if k == fCamel]) == 0
This basically looks at all the elements after the first bCamel and collects all the elements that have the value fCamel. If that list has a length == 0 you have a solution.
Here's a few examples:
>>> k = ['F','F','B','B','F']
>>> solution(k)
False
>>> k = ['F','F','B','B','B']
>>> solution(k)
True
>>> k = ['F','F','B','F','F','B','B']
>>> solution(k)
False
>>>
To build on GWW's answer, if you want this code to work as is you would just do list(reversed(formation)). If you really want to be able to use formation.reverse() instead, you would have to subclass list:
>>> class ReversableList(list):
... def reverse(self):
... return list(reversed(self))
...
>>> x = ReversableList([1,2,3])
>>> x.reverse()
[3, 2, 1]
Whether or not this is advisable is another question of course.
list.reverse reverses inplace. That is:
>>> l = [1, 2, 3]
>>> l.reverse()
>>> l
[3, 2, 1]
Please consult the Python documentation, things like these are laid out there. You can also try the 'help' built-in:
help(l.reverse) Help on built-in function reverse:
reverse(...)
L.reverse() -- reverse IN PLACE
I just came across this problem and wanted to clarify some things for users new to python coming from a javascript background.
In javascript, a.reverse() reverses in place and also returns the array when called.
Javascript:
var a = [2, 3 ,4]
console.log(a.reverse())
// outputs [4, 3, 2]
console.log(a)
// outputs [4, 3, 2]
In python, a.reverse() reverses in place, but does not return the array. This is what caused confusion for me.
In python:
a = [2, 3, 4]
a.reverse()
print(a)
# outputs [4, 3, 2]
# can't do print(a.reverse())
the following changes will be effective:
NumSet={1,2,3,4,5,6,7,8,9,10}
NumList = list(NumSet)
NumList.reverse()
print(NumList)
avoid using the assignment operator after the initial assignment as lists are mutable types.
Using = operator with a method..(eg NumList = NumSet.reverse()) will cause the method to overwrite list with a blank, thereby effectively clearing the list. That's why list becomes a NoneType.
Methods are functions and doesn't actually have its own value, thus the blank.
There are multiple approaches to the problem shown in the OP. A summary:
Use a different technique that gives you a reversed copy of the list, so that this expression can be used in place. The two main ways to do this are formation[::-1] and list(reversed(formation)) (see How to reverse a list?).
Analogous solutions exist to replace other list functionality, for example:
# mylist.append(1)
# mylist.index(1)
(mylist + [1]).index(1) # note that the value is wrapped in another list
# mylist.extend(anotherlist)
# mylist.index(1)
(mylist + anotherlist).index(1)
# mylist.sort()
# mylist.index(1)
sorted(mylist).index(1)
Bite the bullet and use a separate statement anyway. Simple is better than complex; good Python style often avoids long expressions like ((formation.index(bCamel) > (len(formation) - 1 - (formation.reverse()).index(fCamel)))) because it's hard to follow the logic. Keep in mind that since we are still using an in-place method, the original formation is still modified. This can be useful, but it often causes problems.
(Please do not use this in real code.) We can abuse conditional expressions to make the assignment happen as a side effect:
def solution(formation):
return formation.index(bCamel) > (
len(formation) - 1 - (formation.reverse() or formation).index(fCamel)
)
The idea is that since formation.reverse() will return None, which is falsey, so or is forced not to short-circuit, and will evaluate to formation - after the reversal has occurred as a side effect.
Other expressions can have the same net effect, e.g. [formation, formation.reverse()][0]. The idea is to write something that is an expression that includes the .reverse call, but evaluates to the original list object. We can be arbitrarily creative here - but again, Simple is better than complex. Please don't do these things.
Again, keep in mind that this will still modify the original list, which may impact on future calculations.
Rework the logic to avoid the need to reverse the list. The code tries to reverse the list, search in the reversed list for the index of the first match, and then subtract that result from len(formation) - 1 - the overall effect of this is to search for the index of the last match. Lists don't have a method for that, but strings do; and as it happens, all our elements are single-character strings. We could solve the problem more simply by representing the formation with a string:
def solution(formation):
return formation.index(bCamel) > formation.rindex(fCamel)
solution('F BF')
Alternately, we can conceive of the problem differently: "Is there a fCamel in the part of the list that follows the first bCamel?" The accepted answer shows using a list comprehension to iterate over "the part of the list that follows the first bCamel", making a list of all the fCamels there, and checking whether that list is non-empty. But we can do it much more simply:
# works with either the string or list input
def solution(formation):
return fCamel not in formation[formation.index(bCamel)+1:]
(There are even more clever ways to write this, such as Stefan's answer using iterators.)
Solutions like this are specific to the problem solved by the code, and don't answer the question in general. However, it is often possible to find similar solutions in other contexts.
Not super beautiful, but I did not find the equivalent in the precedent answers. If the speed or memory costs are low (the list is not very long or the operation not repeated a huge amount of times), this is pretty straight forward and even easier to read.
import copy
fCamel = 'F'
bCamel = 'B'
gap = ' '
k = ['F', ' ', 'B', 'F']
def solution(formation):
rev_formation = copy.copy(formation)
rev_formation.reverse()
return ((formation.index(bCamel) > (len(formation) - 1 -
(rev_formation).index(fCamel))))
Cheers
This doesn't provide a solution to the F _ B F pattern problem, but it does address the issue of python not returning a list when you use .reverse().
This is how I got around it:
chars = ['a', '-', 'c']
chars2 = [] + chars
chars2.reverse()
totalChars = chars + chars2
totalChars returns a-cc-a, which is what I wanted, AND chars2 is a list, not a pointer to chars. Hope this helps.
I don't know if this works for you, but this works for me:
list = [1,2,3]
print([list, list.reverse()][0])
The reason list.reverse() returns None is because the function doesn't return anything.
Using your code:
fCamel = 'F'
bCamel = 'B'
gap = ' '
k = ['F', ' ', 'B', 'F']
def solution(formation):
return ((formation.index(bCamel) > (len(formation) - 1 - ([formation, formation.reverse()][0]).index(fCamel))))
print(solution(k))
Hope this works for you!
ps. Honestly, I have no idea why this works. I stumbled on it accidentally.
Title question is already answered, but for what you were really after:
Basically, this function should return true if all the 'F's are on the left hand side of the first 'B'
That's the same as there's no 'B' followed by an 'F'. Nice way to check this is with an iterator:
def solution(formation):
it = iter(formation)
return not (bCamel in it and fCamel in it)
Some advantages:
Unlike every formation.index(...) solution it doesn't crash if the searched value isn't there.
Takes only O(1) extra space (unlike the solutions making a reversed copy of the list).
Touches every element at most once and stops as soon as possible. Even has O(1) time best case (even with millions of elements, if the list starts with ['B', 'F', then it stops right there).

How to insert a character after every 2 characters in a string

Is there a pythonic way to insert an element into every 2nd element in a string?
I have a string: 'aabbccdd' and I want the end result to be 'aa-bb-cc-dd'.
I am not sure how I would go about doing that.
>>> s = 'aabbccdd'
>>> '-'.join(s[i:i+2] for i in range(0, len(s), 2))
'aa-bb-cc-dd'
Assume the string's length is always an even number,
>>> s = '12345678'
>>> t = iter(s)
>>> '-'.join(a+b for a,b in zip(t, t))
'12-34-56-78'
The t can also be eliminated with
>>> '-'.join(a+b for a,b in zip(s[::2], s[1::2]))
'12-34-56-78'
The algorithm is to group the string into pairs, then join them with the - character.
The code is written like this. Firstly, it is split into odd digits and even digits.
>>> s[::2], s[1::2]
('1357', '2468')
Then the zip function is used to combine them into an iterable of tuples.
>>> list( zip(s[::2], s[1::2]) )
[('1', '2'), ('3', '4'), ('5', '6'), ('7', '8')]
But tuples aren't what we want. This should be a list of strings. This is the purpose of the list comprehension
>>> [a+b for a,b in zip(s[::2], s[1::2])]
['12', '34', '56', '78']
Finally we use str.join() to combine the list.
>>> '-'.join(a+b for a,b in zip(s[::2], s[1::2]))
'12-34-56-78'
The first piece of code is the same idea, but consumes less memory if the string is long.
If you want to preserve the last character if the string has an odd length, then you can modify KennyTM's answer to use itertools.izip_longest:
>>> s = "aabbccd"
>>> from itertools import izip_longest
>>> '-'.join(a+b for a,b in izip_longest(s[::2], s[1::2], fillvalue=""))
'aa-bb-cc-d'
or
>>> t = iter(s)
>>> '-'.join(a+b for a,b in izip_longest(t, t, fillvalue=""))
'aa-bb-cc-d'
I tend to rely on a regular expression for this, as it seems less verbose and is usually faster than all the alternatives. Aside from having to face down the conventional wisdom regarding regular expressions, I'm not sure there's a drawback.
>>> s = 'aabbccdd'
>>> '-'.join(re.findall('..', s))
'aa-bb-cc-dd'
This version is strict about actual pairs though:
>>> t = s + 'e'
>>> '-'.join(re.findall('..', t))
'aa-bb-cc-dd'
... so with a tweak you can be tolerant of odd-length strings:
>>> '-'.join(re.findall('..?', t))
'aa-bb-cc-dd-e'
Usually you're doing this more than once, so maybe get a head start by creating a shortcut ahead of time:
PAIRS = re.compile('..').findall
out = '-'.join(PAIRS(in))
Or what I would use in real code:
def rejoined(src, sep='-', _split=re.compile('..').findall):
return sep.join(_split(src))
>>> rejoined('aabbccdd', sep=':')
'aa:bb:cc:dd'
I use something like this from time to time to create MAC address representations from 6-byte binary input:
>>> addr = b'\xdc\xf7\x09\x11\xa0\x49'
>>> rejoined(addr[::-1].hex(), sep=':')
'49:a0:11:09:f7:dc'
Here is one list comprehension way with conditional value depending of modulus of enumeration, odd last character will be in group alone:
for s in ['aabbccdd','aabbccdde']:
print(''.join([ char if not ind or ind % 2 else '-' + char
for ind,char in enumerate(s)
]
)
)
""" Output:
aa-bb-cc-dd
aa-bb-cc-dd-e
"""
This one-liner does the trick. It will drop the last character if your string has an odd number of characters.
"-".join([''.join(item) for item in zip(mystring1[::2],mystring1[1::2])])
As PEP8 states:
Do not rely on CPython's efficient implementation of in-place string concatenation for statements in the form a += b or a = a + b. This optimization is fragile even in CPython (it only works for some types) and isn't present at all in implementations.
A pythonic way of doing this that avoids this kind of concatenation, and allows you to join iterables other than strings could be:
':'.join(f'{s[i:i+2]}' for i in range(0, len(s), 2))
And another more functional-like way could be:
':'.join(map('{}{}'.format, *(s[::2], s[1::2])))
This second approach has a particular feature (or bug) of only joining pairs of letters. So:
>>> s = 'abcdefghij'
'ab:cd:ef:gh:ij'
and:
>>> s = 'abcdefghi'
'ab:cd:ef:gh'

Extract certain elements from a list

I have no clue about Python and started to use it on some files. I managed to find out how to do all the things that I need, except for 2 things.
1st
>>>line = ['0', '1', '2', '3', '4', '5', '6']
>>>#prints all elements of line as expected
>>>print string.join(line)
0 1 2 3 4 5 6
>>>#prints the first two elements as expected
>>>print string.join(line[0:2])
0 1
>>>#expected to print the first, second, fourth and sixth element;
>>>#Raises an exception instead
>>>print string.join(line[0:2:4:6])
SyntaxError: invalid syntax
I want this to work similar to awk '{ print $1 $2 $5 $7 }'. How can I accomplish this?
2nd
how can I delete the last character of the line? There is an additional ' that I don't need.
Provided the join here is just to have a nice string to print or store as result (with a coma as separator, in the OP example it would have been whatever was in string).
line = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
print ','.join (line[0:2])
A,B
print ','.join (line[i] for i in [0,1,2,4,5,6])
A,B,C,E,F,G
What you are doing in both cases is extracting a sublist from the initial list. The first one use a slice, the second one use a list comprehension. As others said you could also have accessed to elements one by one, the above syntaxes are merely shorthands for:
print ','.join ([line[0], line[1]])
A,B
print ','.join ([line[0], line[1], line[2], line[4], line[5], line[6]])
A,B,C,E,F,G
I believe some short tutorial on list slices could be helpfull:
l[x:y] is a 'slice' of list l. It will get all elements between position x (included) and position y (excluded). Positions starts at 0. If y is out of list or missing, it will include all list until the end. If you use negative numbers you count from the end of the list. You can also use a third parameter like in l[x:y:step] if you want to 'jump over' some items (not take them in the slice) with a regular interval.
Some examples:
l = range(1, 100) # create a list of 99 integers from 1 to 99
l[:] # resulting slice is a copy of the list
l[0:] # another way to get a copy of the list
l[0:99] # as we know the number of items, we could also do that
l[0:0] # a new empty list (remember y is excluded]
l[0:1] # a new list that contains only the first item of the old list
l[0:2] # a new list that contains only the first two items of the old list
l[0:-1] # a new list that contains all the items of the old list, except the last
l[0:len(l)-1] # same as above but less clear
l[0:-2] # a new list that contains all the items of the old list, except the last two
l[0:len(l)-2] # same as above but less clear
l[1:-1] # a new list with first and last item of the original list removed
l[-2:] # a list that contains the last two items of the original list
l[0::2] # odd numbers
l[1::2] # even numbers
l[2::3] # multiples of 3
If rules to get items are more complex, you'll use a list comprehension instead of a slice, but it's another subjet. That's what I use in my second join example.
You don't want to use join for that. If you just want to print some bits of a list, then specify the ones you want directly:
print '%s %s %s %s' % (line[0], line[1], line[4], line[6])
Assuming that the line variable should contain a line of cells, separated by commas...
You can use map for that:
line = "1,2,3,4,5,6"
cells = line.split(",")
indices=[0,1,4,6]
selected_elements = map( lambda i: cells[i], indices )
print ",".join(selected_elements)
The map function will do the on-the-fly function for each of the indices in the list argument. (Reorder to your liking)
You could use the following using list comprehension :
indices = [0,1,4,6]
Ipadd = string.join([line[i] for i in xrange(len(line)) if i in indices])
Note : You could also use :
Ipadd = string.join([line[i] for i in indices])
but you will need a sorted list of indices without repetition of course.
Answer to the second question:
If your string is contained in myLine, just do:
myLline = myLine[:-1]
to remove the last character.
Or you could also use rstrip():
myLine = myLine.rstrip("'")
>>> token = ':'
>>> s = '1:2:3:4:5:6:7:8:9:10'
>>> sp = s.split(token)
>>> token.join(filter(bool, map(lambda i: i in [0,2,4,6] and sp[i] or False, range(len(sp)))))
'1:3:5:7'
l = []
l.extend(line[0:2])
l.append(line[5]) # fourth field
l.append(line[7]) # sixth field
string.join(l)
Alternatively
"{l[0]} {l[1]} {l[4]} {l[5]}".format(l=line)
Please see PEP 3101 and stop using the % operator for string formatting.

Filtering lists

I want to filter repeated elements in my list
for instance
foo = ['a','b','c','a','b','d','a','d']
I am only interested with:
['a','b','c','d']
What would be the efficient way to do achieve this ?
Cheers
list(set(foo)) if you are using Python 2.5 or greater, but that doesn't maintain order.
Cast foo to a set, if you don't care about element order.
Since there isn't an order-preserving answer with a list comprehension, I propose the following:
>>> temp = set()
>>> [c for c in foo if c not in temp and (temp.add(c) or True)]
['a', 'b', 'c', 'd']
which could also be written as
>>> temp = set()
>>> filter(lambda c: c not in temp and (temp.add(c) or True), foo)
['a', 'b', 'c', 'd']
Depending on how many elements are in foo, you might have faster results through repeated hash lookups instead of repeated iterative searches through a temporary list.
c not in temp verifies that temp does not have an item c; and the or True part forces c to be emitted to the output list when the item is added to the set.
>>> bar = []
>>> for i in foo:
if i not in bar:
bar.append(i)
>>> bar
['a', 'b', 'c', 'd']
this would be the most straightforward way of removing duplicates from the list and preserving the order as much as possible (even though "order" here is inherently wrong concept).
If you care about order a readable way is the following
def filter_unique(a_list):
characters = set()
result = []
for c in a_list:
if not c in characters:
characters.add(c)
result.append(c)
return result
Depending on your requirements of speed, maintanability, space consumption, you could find the above unfitting. In that case, specify your requirements and we can try to do better :-)
If you write a function to do this i would use a generator, it just wants to be used in this case.
def unique(iterable):
yielded = set()
for item in iterable:
if item not in yielded:
yield item
yielded.add(item)
Inspired by Francesco's answer, rather than making our own filter()-type function, let's make the builtin do some work for us:
def unique(a, s=set()):
if a not in s:
s.add(a)
return True
return False
Usage:
uniq = filter(unique, orig)
This may or may not perform faster or slower than an answer that implements all of the work in pure Python. Benchmark and see. Of course, this only works once, but it demonstrates the concept. The ideal solution is, of course, to use a class:
class Unique(set):
def __call__(self, a):
if a not in self:
self.add(a)
return True
return False
Now we can use it as much as we want:
uniq = filter(Unique(), orig)
Once again, we may (or may not) have thrown performance out the window - the gains of using a built-in function may be offset by the overhead of a class. I just though it was an interesting idea.
This is what you want if you need a sorted list at the end:
>>> foo = ['a','b','c','a','b','d','a','d']
>>> bar = sorted(set(foo))
>>> bar
['a', 'b', 'c', 'd']
import numpy as np
np.unique(foo)
You could do a sort of ugly list comprehension hack.
[l[i] for i in range(len(l)) if l.index(l[i]) == i]

Categories

Resources