Python - too many values to unpack - python

I have a tuple containing string and a list of string as below:
test = ('str1',['1', '2'])
for a,b in test:
print(a,b)
I want to unpack in a way that I can get [('str1','1'),('str1','2')].
However I am getting "ValueError: too many values to unpack (expected 2)".
If I print length of test, it comes as 2. So not sure what is wrong here.

Although test has two elements, you're attempting to iterate over tuples which won't work because test has no tuples (it's a tuple itself).
So this works:
test = [('str1',['1', '2'])]
for a,b in test:
print(a,b)
Or, to get what you want, as a list:
print([(test[0], item) for item in test[1]])
You can also iterate in that way:
test = ('str1',['1', '2'])
for item in test[1]:
print(test[0], item)

You can use zip function to implement that:
>>> seq = ['1', '2']
>>> print(zip(['str']*len(seq), seq))
[('str', '1'), ('str', '2')]

"Too many values to unpack" means exactly that. Let's look at the elements in test:
test = ('str1',['1', '2'])
for a,b in test:
print(a,b)
Each element in test will be unpacked into two variables. The first element is 'str1', and the second one is ['1', '2']. 'str1' is a string with four characters, so, unpacked, it would need four variables. However, you only provide two, a and b. That's the error.
To get the output you want, I recommend unpacking as follows:
a,b = test
Now a is 'str1', and b is ['1', '2']. You can then loop through the values in b:
for item in b:
print(a, item)
Result:
str1 1
str1 2

Your code iterates over each item in test; first 'str1', then ['1', '2']. The problem is when you try to do a, b = 'str1' (this is what the for loop is doing). There are 4 values in 'str1', but only two variables to which you're trying to assign them.
Here's one way to do what you actually want:
test = ('str1',['1', '2'])
test_str, test_list = test
for b in test_list:
print(test_str, b)
In this code, test_str is 'str1' and test_list is ['1', '2']. Then you iterate over test_list, and just reference test_str to get 'str1'.

Related

Find index of an element in list using wildcards

I have a list like this:
a = [['1','2','3','a','b'],
['4','5','6','c','d'],
['7','8','9','e','f']]
I want to identify the index of an element in the list a based on first three sub-elements of the element. For example, the index of the element which contains ['4','5','6'] as its first three sub-elements is 1.
I have tried to do this using list comprehension follows:
ind = [i for i in range(0,len(a)) if (a[i][0] == '4' and a[i][1] == '5' and a[i][2] == '6')]
But this is computationally expensive as I have to implement this code several times in a for loop. So, I am looking for a less computationally expensive method. SO far I have tried 'list.index' as follows:
ind = a.index(['4','5','6','*','*'])
where '*' is used as wildcard string. But is does not work as it outputs:
['4', '5', '6', '.*', '.*'] is not in list
I think there is something wrong with the way I am using wildcard. Can you please tell me what is it? Or is there another fast way to identify the element of a list based on its sub-elements?
Solution 1: True wildcard
You can simply use a.index(...) if you use a true wildcard:
class Wildcard:
def __eq__(self, anything):
return True
a = [['1','2','3','a','b'],
['4','5','6','c','d'],
['7','8','9','e','f']]
wc = Wildcard()
print(a.index(['4', '5', '6', wc, wc]))
Outputs 1. Try it online!
This might be fast because 1) it does the searching in C code and 2) it does minimal work, as it for example doesn't create a list slice for every row and might often rule out a row simply by looking at just the first value.
Solution 2: operator.indexOf
Or using operator.indexOf, which finds the index in C rather than in Python like an enumerate solution would. Here are two versions of that, I suspect the one mapping a ready-to-go slice is faster:
from operator import indexOf, itemgetter
a = [['1','2','3','a','b'],
['4','5','6','c','d'],
['7','8','9','e','f']]
print(indexOf((r[:3] for r in a), ['4', '5', '6']))
print(indexOf(map(itemgetter(slice(3)), a), ['4', '5', '6']))
Try it online!
Well you could transpose and slice and transpose back and finally index, like this:
>>> list(zip(*list(zip(*a))[:3])).index(('4', '5', '6'))
1
>>>
But what's wrong with?
>>> [x[:3] for x in a].index(['4', '5', '6'])
1
>>>
It may be more efficient to implement it as a generator. In this way, if e.g. you know that you can get at most one match you can stop once it is found:
a = [['1','2','3','a','b'],
['4','5','6','c','d'],
['7','8','9','e','f']]
pattern = ['4','5','6']
def find_index(data, pattern):
for n, elt in enumerate(a):
if elt[:3] == pattern:
yield n
indices = find_index(a, pattern)
next(indices)
It gives:
1
Here's a fairly simple and straightforward way of accomplishing this:
def findListMatch(lists, match):
# Loop over lists with indices
for k, sublist in enumerate(lists):
# Check if the beginning of the list matches the desired start pattern
if sublist[0:len(match)] == match:
# If it's a match, return the index
return k
# If none of the lists match, return a placeholder value of "None"
return None
a = [['1','2','3','a','b'],
['4','5','6','c','d'],
['7','8','9','e','f']]
matchIndex = findListMatch(a, ['4', '5', '6'])
# Result:
# matchIndex = 1
#U12-Forward's answer works but unnecessarily builds a temporary list of the same size as the input list before applying the index method, which can be quite an overhead if the list is long. A more efficient approach would be to use enumerate to generate the indices while comparing the first 3 items to the desired list:
next(i for i, l in enumerate(a) if l[:3] == ['4', '5', '6'])

Get max number out of strings

I have the following 'numbers', which are actually strings from a regex:
nums = ['1', '4', '9', '10']
I would like to get the max, which would be 10. Is there a cleaner way to do this than to do a list comprehension, such as:
>>> max(nums)
'9'
>>> max([int(item) for item in nums])
10
Otherwise, it would give me 9.
max has a keyword argument key that takes a callable which transforms the values in some way before comparing them.
>>> max(nums, key=int)
This is essentially the same as your list comprehension max(int(item) for item in nums), except that it's important to note that the original values are returned, not the transformed values. This means that:
>>> a = max(nums, key=int)
>>> b = max(int(item) for item in nums)
>>> repr(a), repr(b)
('10', 10)
Use the map function:
>>> max(map(int, nums))

print comma separated if more than two values in the list python

My input1:
values = ['1','2']
Expected output to print
print 1, 2
my input2:
values = ['1']
Expected output to print
print 1
my input3:
values = ['1','2','3']
Expected output to print
print 1,2,3
Below is what i tried:
for x in values:
print x
Just call join passing in your list and if it is only one element, it won't add the "comma":
print(','.join(['1']))
output:
1
print(','.join(['1', '2']))
output:
1,2
print(','.join(['1', '2', '3']))
output:
1,2,3
If you have a list of integers, or a mix of strings and integers, then you would have to call str on the integer parts in your list. However, the easiest way to go about doing this would be to either call map on your list (map will apply a callable to each item in your list) to cast to the appropriate str, or perform a generator comprehension to cast to int:
comprehension:
print(",".join(str(i) for i in [1,2,3]))
map:
print(",".join(map(str, [1,2,3])))
Just simple as:
print(','.join(myList))
What you type in the command for print isn't exactly what comes out. Basically the commas in the print command just separate out each item you asked it to print but don't tell it to print commas itself. i.e.
>>> print 1, 2, 3
1 2 3
The key is to create the text or string how you want it to look and then print that.
>>> text = ','.join(str(x) for x in [1, 2, 3])
>>> print text
1,2,3

Python slice first and last element in list

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).

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.

Categories

Resources