pythonic method to find the inner value of a list - python

I have the task to either return the element of a list if there is only one element in it, or to return the list itself. The method I am using works for most of my use cases but not for strings.
def inner_value(somelist):
'''
Return somelist[0] if it's a one-element list or the whole list otherwise
:param list somelist: list that might contain only one element
:returns: element of somelist or somelist
>>> inner_value([42])
42
>>> inner_value([42,43])
[42, 43]
>>> inner_value([[42]])
42
>>> inner_value([[42,43]])
[42, 43]
>>> inner_value('spam')
'spam'
>>> inner_value(['spam'])
'spam'
>>> inner_value(['spam','eggs','bacon'])
['spam', 'eggs', 'bacon']
.. warning::
This method does not work for dictionaries (KeyError)
or single character strings (infinite recursion)!
'''
try:
if len(somelist) == 1:
return inner_value(somelist[0])
else:
return somelist
except TypeError:
return somelist
Is there a better way of doing it such that
inner_value('spam') == 'spam'?

You could check the variable type using isinstance method:
if isinstance(somelist, list):
if len(somelist) == 1:
return somelist[0]
else:
return somelist
else:
return somelist
If you only check the len, it would cause problems, since len would give the size of string too:
>>> len(['first','second','third'])
3
>>> len('arbitrary')
9

Related

Is there a way to set "array of strings" as a type for a parameter in a function?

I want to check right at the passing of the arguments to a function if the argument is an array of strings.
Like setting a type to the parameter of the function to "array of string".
But I don't want to loop through the array looking for none-string elements.
Is there a type like this?
You can use typing for recent python version (tested on 3.9), as suggested by #Netwave in the comments:
def my_function(a: list[str]):
# You code
Which will lint as expected:
>>> isinstance(["abc", "def", "ghi", "jkl"], list)
True
>>> isinstance(50, list)
False
You could use this inside your function in order to check if your argument is a list.
Will a lambda function work?
def check_arr_str(li):
#Filter out elements which are of type string
res = list(filter(lambda x: isinstance(x,str), li))
#If length of original and filtered list match, all elements are strings, otherwise not
return (len(res) == len(li) and isinstance(li, list))
The outputs will look like
print(check_arr_str(['a','b']))
#True
print(check_arr_str(['a','b', 1]))
#False
print(check_arr_str(['a','b', {}, []]))
#False
print(check_arr_str('a'))
#False
If an exception is needed, we can change the function as follows.
def check_arr_str(li):
res = list(filter(lambda x: isinstance(x,str), li))
if (len(res) == len(li) and isinstance(li, list)):
raise TypeError('I am expecting list of strings')
Another way we can do this is using any to check if we have any item in the list which is not a string, or the parameter is not a list (Thanks #Netwave for the suggestion)
def check_arr_str(li):
#Check if any instance of the list is not a string
flag = any(not isinstance(i,str) for i in li)
#If any instance of an item in the list not being a list, or the input itself not being a list is found, throw exception
if (flag or not isinstance(li, list)):
raise TypeError('I am expecting list of strings')
The way to make it bullet proof is to check them in the function (sadly iterating over the elements), but using all with a comprehension makes the evaluation lazy and will stop in the first element that is not a string instance:
def foo(my_str_list):
is_list = isinstance(my_str_list, list)
are_strings = all(isinstance(x, str) for x in my_str_list)
if not is_list or not are_strings:
raise TypeError("Funtion argument should be a list of strings.")
...
Try this:
l = ["abc", "def", "ghi", "jkl"]
isinstance(l, list) and all(isinstance(i,str) for i in l)
the output:
In [1]: a = ["abc", "def", "ghi", "jkl"]
In [2]: isinstance(a, list) and all(isinstance(i,str) for i in a)
Out[2]: True
In [3]: a = ["abc", "def", "ghi", "jkl",2]
In [4]: isinstance(a, list) and all(isinstance(i,str) for i in a)
Out[4]: False

flattening a list or a tuple in python. Not sure what the error is

def flatten(t):
list = []
for i in t:
if(type(i) != list and type(i) != tuple):
list.append(i)
else:
list.extend(flatten(i))
return list
Here is the function that I've written to flatten a list or a tuple that looks something like
l = ((5, (1,2), [[1],[2]]))
when I do
flatten(l)
I expected to get a list
[5, 1, 2, 1, 2]
but I instead get
[5, 1, 2, [[1], [2]]]
I am not really sure why this happens because
else:
list.extend(flatten(i))
this part of the code will retrieve the int inside the third element of the tuple which is [[1],[2]] by calling it recursively. What am I missing here?
Thanks
You've assigned the built-in list to a list instance in your function, so the type check no longer works properly for list objects.
You should use a different name for the accumulator list, say, lst:
def flatten(t):
lst = []
for i in t:
if type(i) != list and type(i) != tuple:
lst.append(i)
else:
lst.extend(flatten(i))
return lst
OTOH, you could do both checks at once using isinstance which also ensures that the objects you're running your checks against are actually types:
...
if not isinstance(i, (list, tuple)):
...
You masked the name list:
list = []
...
if(type(i) != list and ...
type(i) is never going to be equal to [].
Use a different name for your list:
result = []
You also want to use isinstance() instead of type() with equality tests:
def flatten(t):
result = []
for i in t:
if not isinstance(i, (list, tuple)):
result.append(i)
else:
result.extend(flatten(i))
return list
this would have told you much earlier that you were not comparing with a type:
>>> list = []
>>> isinstance(['foo'], (list, tuple))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: isinstance() arg 2 must be a type or tuple of types
The exception is thrown because [] is not a type.
You could use a generator here to avoid the need to append or extend a new list object repeatedly. If you put the isinstance() type check outside the loop you can support non-sequences with the same function:
def flatten(t):
if not isinstance(t, (list, tuple)):
yield t
return
for i in t:
yield from flatten(i)
result = list(flatten(some_sequence_or_single_value))

Modifying a list outside of recursive function in Python

There is a multidimensional list with not clear structure:
a=[[['123', '456'], ['789', '1011']], [['1213', '1415']], [['1617', '1819']]]
And there is a recursive function, which operates the list:
def get_The_Group_And_Rewrite_It(workingList):
if isinstance(workingList[0],str):
doSomething(workingList)
else:
for i in workingList:
get_The_Group_And_Rewrite_It(i)
Through time the get_The_Group_And_Rewrite_It() should get a list, for instance, ['123','456'], and as soon as it get it, doSomething function should rewrite it with ['abc'] in entire list.
The same with other lists of format[str,str,...]. In the end I should get something like
a=[[['abc'], ['abc']], [['abc']], [['abc']]]
I see it would be easy in C++, using *links, but how to do that in Python?
For this case, you can use slice assignment:
>>> a = [[['123', '456']]]
>>> x = a[0][0]
>>> x[:] = ['abc']
>>> a
[[['abc']]]
>>> def f(workingList):
... if isinstance(workingList[0],str):
... workingList[:] = ['abc']
... else:
... for i in workingList:
... f(i)
...
>>> a=[[['123', '456'], ['789', '1011']], [['1213', '1415']], [['1617', '1819']]]
>>> f(a)
>>> a
[[['abc'], ['abc']], [['abc']], [['abc']]]

Is there a short contains function for lists?

Given a list xs and a value item, how can I check whether xs contains item (i.e., if any of the elements of xs is equal to item)? Is there something like xs.contains(item)?
For performance considerations, see Fastest way to check if a value exists in a list.
Use:
if my_item in some_list:
...
Also, inverse operation:
if my_item not in some_list:
...
It works fine for lists, tuples, sets and dicts (check keys).
Note that this is an O(n) operation in lists and tuples, but an O(1) operation in sets and dicts.
In addition to what other have said, you may also be interested to know that what in does is to call the list.__contains__ method, that you can define on any class you write and can get extremely handy to use python at his full extent.
A dumb use may be:
>>> class ContainsEverything:
def __init__(self):
return None
def __contains__(self, *elem, **k):
return True
>>> a = ContainsEverything()
>>> 3 in a
True
>>> a in a
True
>>> False in a
True
>>> False not in a
False
>>>
I came up with this one liner recently for getting True if a list contains any number of occurrences of an item, or False if it contains no occurrences or nothing at all. Using next(...) gives this a default return value (False) and means it should run significantly faster than running the whole list comprehension.
list_does_contain = next((True for item in list_to_test if item == test_item), False)
The list method index will return -1 if the item is not present, and will return the index of the item in the list if it is present. Alternatively in an if statement you can do the following:
if myItem in list:
#do things
You can also check if an element is not in a list with the following if statement:
if myItem not in list:
#do things
There is also the list method:
[2, 51, 6, 8, 3].__contains__(8)
# Out[33]: True
[2, 51, 6, 3].__contains__(8)
# Out[33]: False
There is one another method that uses index. But I am not sure if this has any fault or not.
list = [5,4,3,1]
try:
list.index(2)
#code for when item is expected to be in the list
print("present")
except:
#code for when item is not expected to be in the list
print("not present")
Output:
not present

How do I get the last element of a list?

How do I get the last element of a list?
Which way is preferred?
alist[-1]
alist[len(alist) - 1]
some_list[-1] is the shortest and most Pythonic.
In fact, you can do much more with this syntax. The some_list[-n] syntax gets the nth-to-last element. So some_list[-1] gets the last element, some_list[-2] gets the second to last, etc, all the way down to some_list[-len(some_list)], which gives you the first element.
You can also set list elements in this way. For instance:
>>> some_list = [1, 2, 3]
>>> some_list[-1] = 5 # Set the last element
>>> some_list[-2] = 3 # Set the second to last element
>>> some_list
[1, 3, 5]
Note that getting a list item by index will raise an IndexError if the expected item doesn't exist. This means that some_list[-1] will raise an exception if some_list is empty, because an empty list can't have a last element.
If your str() or list() objects might end up being empty as so: astr = '' or alist = [], then you might want to use alist[-1:] instead of alist[-1] for object "sameness".
The significance of this is:
alist = []
alist[-1] # will generate an IndexError exception whereas
alist[-1:] # will return an empty list
astr = ''
astr[-1] # will generate an IndexError exception whereas
astr[-1:] # will return an empty str
Where the distinction being made is that returning an empty list object or empty str object is more "last element"-like then an exception object.
You can also do:
last_elem = alist.pop()
It depends on what you want to do with your list because the pop() method will delete the last element.
The simplest way to display last element in python is
>>> list[-1:] # returns indexed value
[3]
>>> list[-1] # returns value
3
there are many other method to achieve such a goal but these are short and sweet to use.
In Python, how do you get the last element of a list?
To just get the last element,
without modifying the list, and
assuming you know the list has a last element (i.e. it is nonempty)
pass -1 to the subscript notation:
>>> a_list = ['zero', 'one', 'two', 'three']
>>> a_list[-1]
'three'
Explanation
Indexes and slices can take negative integers as arguments.
I have modified an example from the documentation to indicate which item in a sequence each index references, in this case, in the string "Python", -1 references the last element, the character, 'n':
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5
-6 -5 -4 -3 -2 -1
>>> p = 'Python'
>>> p[-1]
'n'
Assignment via iterable unpacking
This method may unnecessarily materialize a second list for the purposes of just getting the last element, but for the sake of completeness (and since it supports any iterable - not just lists):
>>> *head, last = a_list
>>> last
'three'
The variable name, head is bound to the unnecessary newly created list:
>>> head
['zero', 'one', 'two']
If you intend to do nothing with that list, this would be more apropos:
*_, last = a_list
Or, really, if you know it's a list (or at least accepts subscript notation):
last = a_list[-1]
In a function
A commenter said:
I wish Python had a function for first() and last() like Lisp does... it would get rid of a lot of unnecessary lambda functions.
These would be quite simple to define:
def last(a_list):
return a_list[-1]
def first(a_list):
return a_list[0]
Or use operator.itemgetter:
>>> import operator
>>> last = operator.itemgetter(-1)
>>> first = operator.itemgetter(0)
In either case:
>>> last(a_list)
'three'
>>> first(a_list)
'zero'
Special cases
If you're doing something more complicated, you may find it more performant to get the last element in slightly different ways.
If you're new to programming, you should avoid this section, because it couples otherwise semantically different parts of algorithms together. If you change your algorithm in one place, it may have an unintended impact on another line of code.
I try to provide caveats and conditions as completely as I can, but I may have missed something. Please comment if you think I'm leaving a caveat out.
Slicing
A slice of a list returns a new list - so we can slice from -1 to the end if we are going to want the element in a new list:
>>> a_slice = a_list[-1:]
>>> a_slice
['three']
This has the upside of not failing if the list is empty:
>>> empty_list = []
>>> tail = empty_list[-1:]
>>> if tail:
... do_something(tail)
Whereas attempting to access by index raises an IndexError which would need to be handled:
>>> empty_list[-1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
But again, slicing for this purpose should only be done if you need:
a new list created
and the new list to be empty if the prior list was empty.
for loops
As a feature of Python, there is no inner scoping in a for loop.
If you're performing a complete iteration over the list already, the last element will still be referenced by the variable name assigned in the loop:
>>> def do_something(arg): pass
>>> for item in a_list:
... do_something(item)
...
>>> item
'three'
This is not semantically the last thing in the list. This is semantically the last thing that the name, item, was bound to.
>>> def do_something(arg): raise Exception
>>> for item in a_list:
... do_something(item)
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 1, in do_something
Exception
>>> item
'zero'
Thus this should only be used to get the last element if you
are already looping, and
you know the loop will finish (not break or exit due to errors), otherwise it will point to the last element referenced by the loop.
Getting and removing it
We can also mutate our original list by removing and returning the last element:
>>> a_list.pop(-1)
'three'
>>> a_list
['zero', 'one', 'two']
But now the original list is modified.
(-1 is actually the default argument, so list.pop can be used without an index argument):
>>> a_list.pop()
'two'
Only do this if
you know the list has elements in it, or are prepared to handle the exception if it is empty, and
you do intend to remove the last element from the list, treating it like a stack.
These are valid use-cases, but not very common.
Saving the rest of the reverse for later:
I don't know why you'd do it, but for completeness, since reversed returns an iterator (which supports the iterator protocol) you can pass its result to next:
>>> next(reversed([1,2,3]))
3
So it's like doing the reverse of this:
>>> next(iter([1,2,3]))
1
But I can't think of a good reason to do this, unless you'll need the rest of the reverse iterator later, which would probably look more like this:
reverse_iterator = reversed([1,2,3])
last_element = next(reverse_iterator)
use_later = list(reverse_iterator)
and now:
>>> use_later
[2, 1]
>>> last_element
3
To prevent IndexError: list index out of range, use this syntax:
mylist = [1, 2, 3, 4]
# With None as default value:
value = mylist and mylist[-1]
# With specified default value (option 1):
value = mylist and mylist[-1] or 'default'
# With specified default value (option 2):
value = mylist[-1] if mylist else 'default'
lst[-1] is the best approach, but with general iterables, consider more_itertools.last:
Code
import more_itertools as mit
mit.last([0, 1, 2, 3])
# 3
mit.last(iter([1, 2, 3]))
# 3
mit.last([], "some default")
# 'some default'
Another method:
some_list.reverse()
some_list[0]
Here is the solution for your query.
a=["first","second","second from last","last"] # A sample list
print(a[0]) #prints the first item in the list because the index of the list always starts from 0.
print(a[1]) #prints second item in list
print(a[-1]) #prints the last item in the list.
print(a[-2]) #prints the second last item in the list.
Output:
>>> first
>>> second
>>> last
>>> second from last
list[-1] will retrieve the last element of the list without changing the list.
list.pop() will retrieve the last element of the list, but it will mutate/change the original list. Usually, mutating the original list is not recommended.
Alternatively, if, for some reason, you're looking for something less pythonic, you could use list[len(list)-1], assuming the list is not empty.
You can also use the code below, if you do not want to get IndexError when the list is empty.
next(reversed(some_list), None)
Ok, but what about common in almost every language way items[len(items) - 1]? This is IMO the easiest way to get last element, because it does not require anything pythonic knowledge.
Strange that nobody posted this yet:
>>> l = [1, 2, 3]
>>> *x, last_elem = l
>>> last_elem
3
>>>
Just unpack.
You can use ~ operator to get the ith element from end (indexed from 0).
lst=[1,3,5,7,9]
print(lst[~0])
Accessing the last element from the list in Python:
1: Access the last element with negative indexing -1
>> data = ['s','t','a','c','k','o','v','e','r','f','l','o','w']
>> data[-1]
'w'
2. Access the last element with pop() method
>> data = ['s','t','a','c','k','o','v','e','r','f','l','o','w']
>> data.pop()
'w'
However, pop method will remove the last element from the list.
METHOD 1:
L = [8, 23, 45, 12, 78]
print(L[len(L)-1])
METHOD 2:
L = [8, 23, 45, 12, 78]
print(L[-1])
METHOD 3:
L = [8, 23, 45, 12, 78]
L.reverse()
print(L[0])
METHOD 4:
L = [8, 23, 45, 12, 78]
print(L[~0])
METHOD 5:
L = [8, 23, 45, 12, 78]
print(L.pop())
All are outputting 78
To avoid "IndexError: list index out of range", you can use this piece of code.
list_values = [12, 112, 443]
def getLastElement(lst):
if len(lst) == 0:
return 0
else:
return lst[-1]
print(getLastElement(list_values))
Pythonic Way
So lets consider that we have a list a = [1,2,3,4], in Python List can be manipulated to give us part of it or a element of it, using the following command one can easily get the last element.
print(a[-1])
You can also use the length to get the last element:
last_elem = arr[len(arr) - 1]
If the list is empty, you'll get an IndexError exception, but you also get that with arr[-1].
If you use negative numbers, it will start giving you elements from last of the list
Example
lst=[1,3,5,7,9]
print(lst[-1])
Result
9
If you do my_list[-1] this returns the last element of the list. Negative sequence indexes represent positions from the end of the array. Negative indexing means beginning from the end, -1 refers to the last item, -2 refers to the second-last item, etc.
You will just need to take the and put [-1] index. For example:
list=[0,1,2]
last_index=list[-1]
print(last_index)
You will get 2 as the output.
You could use it with next and iter with [::-1]:
>>> a = [1, 2, 3]
>>> next(iter(a[::-1]))
3
>>>
array=[1,2,3,4,5,6,7]
last_element= array[len(array)-1]
last_element
Another simple solution
Couldn't find any answer mentioning this. So adding.
You could try some_list[~0] also.
That's the tilde symbol

Categories