Python create a list that contains tuples [duplicate] - python

For example, I have three lists (of the same length)
A = [1,2,3]
B = [a,b,c]
C = [x,y,z]
and i want to merge it into something like:
[[1,a,x],[2,b,y],[3,c,z]].
Here is what I have so far:
define merger(A,B,C):
answer =
for y in range (len(A)):
a = A[y]
b = B[y]
c = C[y]
temp = [a,b,c]
answer = answer.extend(temp)
return answer
Received error:
'NoneType' object has no attribute 'extend'

It looks like your code is meant to say answer = [], and leaving that out will cause problems. But the major problem you have is this:
answer = answer.extend(temp)
extend modifies answer and returns None. Leave this as just answer.extend(temp) and it will work. You likely also want to use the append method rather than extend - append puts one object (the list temp) at the end of answer, while extend appends each item of temp individually, ultimately giving the flattened version of what you're after: [1, 'a', 'x', 2, 'b', 'y', 3, 'c', 'z'].
But, rather than reinventing the wheel, this is exactly what the builtin zip is for:
>>> A = [1,2,3]
>>> B = ['a', 'b', 'c']
>>> C = ['x', 'y', 'z']
>>> list(zip(A, B, C))
[(1, 'a', 'x'), (2, 'b', 'y'), (3, 'c', 'z')]
Note that in Python 2, zip returns a list of tuples; in Python 3, it returns a lazy iterator (ie, it builds the tuples as they're requested, rather than precomputing them). If you want the Python 2 behaviour in Python 3, you pass it through list as I've done above. If you want the Python 3 behaviour in Python 2, use the function izip from itertools.

To get a list of lists, you can use the built-in function zip() and list comprehension to convert each element of the result of zip() from a tupleto a list:
A = [1, 2, 3]
B = [4, 5, 6]
C = [7, 8, 9]
X = [list(e) for e in zip(A, B, C,)]
print X
>>> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Assuming you are doing this for class and not learning all of the tricks that make Python a great tool here is what you need. You had two problems, first if you want to extend then you do it in place but your desired result shows that you want to append, not extend
def merger(A,B,C):
answer = []
for y in range (len(A)):
a=A[y]
b=B[y]
c=C[y]
temp = [a,b,c]
answer.append(temp)
return answer
>>> answer
[[1, 'a', 'x'], [2, 'b', 'y'], [3, 'c', 'z']]

I was just wondering the same thing. I'm a total noob using code academy. This is what i came up to combine two lists index at index
toppings = ['pepperoni', 'pineapple', 'cheese', 'sausage', 'olives', 'anchovies', 'mushrooms']
prices = [2,6,1,3,2,7,2]
num_pizzas = len(toppings)
print("We sell "+str(num_pizzas)+" different kinds of pizza!")
***pizzas = list(zip(toppings, prices))***
print (pizzas)
the list pizzas printed out ...[('pepperoni', 2), ('pineapple', 6), ('cheese', 1), ('sausage', 3), ('olives', 2), ('anchovies', 7), ('mushrooms', 2)]

Related

If/Else Statements Inside a List Definition?

I've been using Python for a long time, but I've recently discovered a new way of creating and iterating through lists:
l = [x for x in range(0, 10)]
This produces a list from 0 to 9. It's very useful, and much simpler than what I had been doing before:
l = []
for x in range(0, 9):
l.append(x)
Now that I understand this way of doing things, I've been using it for a lot of stuff, and it's working out great. The only problem is, I sometimes want another option. For example, if I have a list full of 1's and 0's:
import random
l = [random.randint(0, 1) for i in range(0, 10)]
I want to take this list and iterate through again, only do two different things based on an if else statement:
for idx, item in enumerate(l):
if item == 1:
l[idx] = 'A'
else:
l[idx] = 'B'
I know it would be easier to just create a list full of random instances of 'A' and 'B', but this is just an example, it won't work in my use case. How would I use my new way of creating lists to do this? I know how to add an if statement on the end:
l = [x for x in range(0, 10)]
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
l = [x for x in range(0, 10) if i != 7]
# [0, 1, 2, 3, 4, 5, 6, 8, 9]
But what about my else statement? And, incidentally, what is this way of creating lists called? I might have been able to find an answer online if I knew.
What you want to do is called list comprehension and it is very helpful indeed!
This post answers your question: if/else in a list comprehension
And in your example, you want to do:
L = ['A' if random.randint(0, 1)==1 else 'B' for i in range(0, 10)]
Ok so let's clear some things up. Firstly, this way of creating lists is called list comprehension.
Now, let's talk about how to achieve a list of A's and B's(by list comprehension):
import random
# This creates a list of 1's and 0's
l = [random.randint(0, 1) for i in range(0, 10)]
# [1, 1, 1, 0, 0, 1, 1, 1, 1, 0]
# This creates a list of A's and B's
l = ['A' if num == 1 else 'B' for num in l]
#['A', 'A', 'A', 'B', 'B', 'A', 'A', 'A', 'A', 'B']
How to do it normally (without list comprehension)?
import random
l = []
for x in range(0, 10): # This should be 10 instead of 9 to get 10 values.
l.append(random.randint(0,1))
# l = [0, 1, 0, 1, 1, 0, 0, 0, 0, 1]
for idx, item in enumerate(l): # You need to use `enumerate()` to get index, value
if item == 1:
l[idx] = 'A'
else:
l[idx] = 'B'
# l = ['B', 'A', 'B', 'A', 'A', 'B', 'B', 'B', 'B', 'A']
You could make use of Python's ternary operator:
l = ['A' if i == 1 else 'B' for i in l]
The method of creating lists that you used is called a "list generator". Similarly, there is a dictionary generator.
To solve your problem, you need to add conditions inside the list generator:
import random
new_list = [random.randint(0, 1) for i in range(0, 10)]
another_new_list = ['A' if number == 1 else 'B' for number in new_list]

How would I achieve this dict comprehension of two keys with list values using zip?

I've been trying for the past while to convert a code snippet I have into code comprehension, not because it is faster or more beneficial, but because it would help me understand Python more.
I have a generator that I'm reading from that contains 2 values, 1 value for EN, and 1 value for AR (Languages). I want to map the values inside into a new dictionary with 2 keys EN and AR that contains the list of values per language. The languages are specified from a config file under the name SupportedLanguages: SupportedLanguages = ["en", "ar"].
I know that in Javascript something similar can be done using the map and reduce functions, but I'm unsure how to do that here in Python, and nothing of what I've tried seems to be doing what I want.
Here is the current code snippet that I have:
reader = ([i, str(i)] for i in range(10))
rows = [row for row in reader]
SupportedLanguages = ["en", "ar"]
dict_ = {
"en": []
"ar": []
}
for row in rows:
for i, _ in enumerate(row):
dict_[SupportedLanguages[i]].append(row[i])
reader is a dummy generator to replicate the behavior of my csv reader (since I have n rows, where n depends on the number of languages I have).
The working solution that I have come up with is this comprehension:
{SupportedLanguages[i]: [row[i] for row in rows] for i in range(len(row))}
However, this is looping over rows twice, and I don't want this behavior, I just need a single loop given that the generator will only loop once as well (csv.reader). I know that I can store all the values into a list before looping over them, but then again this creates n loops, and I only want 1.
Edit:
As I mentioned above it is creating 2 loops, but what I should've said is n loops, as in 1 loop per language.
I think I understand what you're trying to achieve. Let's say you have a reader that looks like this:
reader = ([en_value, ar_value] for en_value, ar_value in enumerate("abcdefg"))
for pair in reader: # Don't actually do this part - print just for demonstration
print(pair)
Output:
[0, 'a']
[1, 'b']
[2, 'c']
[3, 'd']
[4, 'e']
[5, 'f']
[6, 'g']
>>>
Then you can construct your dict_ in the following way:
supported_languages = ["en", "ar"]
dict_ = dict(zip(supported_languages, zip(*reader)))
print(dict_)
Output:
{'en': (0, 1, 2, 3, 4, 5, 6), 'ar': ('a', 'b', 'c', 'd', 'e', 'f', 'g')}
>>>
Based on the observation that...:
a, b = zip(*reader)
print(a)
print(b)
...yields:
(0, 1, 2, 3, 4, 5, 6)
('a', 'b', 'c', 'd', 'e', 'f', 'g')
>>>

Python List indexing multiple ranges

Sorry if this has already been asked, I couldn't find it anywhere. Basically how do I get 2 separate ranges within a list in Python.
If I want the 1st, 2nd, 5th and 6th elements of a list I know I can do this,
l = range(0,15)
l[1:3]+l[5:7]
but this assumes that l is easy to write. However I am scrapping something from a webpage using BeautifulSoup4, so I'm using soup.find_all (which gives me a list), so I can't simply write out 2 lists, l and concatenate them.
I want an answer that is something like
l = range(0,15)
l[1:3,5:7]
(but of course without the error) :)
This might be what you want. itemgetter creates a function that retrieves the listed indices:
>>> import operator
>>> snip = operator.itemgetter(1,2,5,6)
>>> snip(range(15))
(1, 2, 5, 6)
>>> snip('abcdefg')
('b', 'c', 'f', 'g')
>>> snip([1,2,3,4,5,6,7,8])
(2, 3, 6, 7)
I would do this with a function:
def multi_range(l, *args):
output = []
for indices in args:
output += l[indices[0]:indices[1]]
return output
So the first argument would be the list, and the rest of the parameters are tuples with the indices you're looking to pull. It would work fine with a long list name:
long_list_name = range(0, 15)
print multi_range(long_list_name, (1, 3), (5, 7))
>>> [1, 2, 5, 6]
l = range(0, 15)
print([l[i] for i in [1,2, 5,6]])
Not sure why you think l[1:3]+l[5:7] is hard, find_all returns a normal python list like any other.
Or using map:
l = range(0, 15)
print(list(map(l.__getitem__,(1,2,5,6))))
Is this OK?
indices = [1, 2, 5, 6]
selected = [l[i] for i in indices]

Python: Insert a string after each element of an array with an exception

I have the following Python script. What I want to do is add several strings to random.shuffle(scenArray). Specifically, there will be a string after each element of the array, however, the 8th element in the array will need a different string.
E.g. if the array is
1,2,3,4,5,6,7,8,9 I want to make it 1,A,2,A,3,A,4,A,5,A,6,A,7,A,8,B,9,A
Any help greatly appreciated.
import random
# General comment: some of the script might be confusing because python
# uses zero-based numbering to index arrays
# read in the full list of scenario x conditions
f = open('scenarioList.xml', 'U')
data = f.read()
f.close()
inst = data.split("\n\n")
# This specifies which scenarios are in which counterbalancing group
cGroups = [[0,1,2,3,4],
[5,6,7,8,9],
[10,11,12,13,14]]
conds = [inst[0:15],inst[15:30],inst[30:45]] # the xml strings divided up by condition
# this is the counterbalancing scheme (latin square)
cScheme = [[1,2,3],
[1,3,2],
[2 ,1 , 3],
[2 , 3 , 1],
[3 , 1 , 2],
[3, 2 , 1]]
# change the second index in the range to loop up to 60; set to 12 now for testing
for subj in range(1,12):
cRow = cScheme[(subj-1)%6] # use the modulus operator to find out which row to use in counterbalancing table
scenArray = []
# loop across scenario groups and look up their assigned interruption condition for this subj
for group in range(0,3):
#conds[cScheme[group]][i]
scenArray.extend([ conds[cRow[group]-1][i] for i in cGroups[group]]) # use extend and not append here
# randomize order of arrays---this is something you might modify to control this a bit more
random.shuffle(scenArray)
f = open('scenarios' + str(subj) + 'xml', 'w')
f.write('\r\n\r\n'.join(scenArray))
f.close()
Ignoring all your code, but from your description and example:
E.g. if the array is 1,2,3,4,5,6,7,8,9
I want to make it 1,A,2,A,3,A,4,A,5,A,6,A,7,A,8,B,9,A
You could do something like:
lst1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
lst2 = sum(([x,'A'] if i != 7 else [x,'B'] for (i,x) in enumerate(lst1)), [])
print lst2 # [1, 'A', 2, 'A', 3, 'A', 4, 'A', 5, 'A', 6, 'A', 7, 'A', 8, 'B', 9, 'A']
EDIT
The one-liner that is assigned to lst2 can be equivalently re-written as:
lst3 = [] # Initialize an empty list
for (i,x) in enumerate(lst1):
if i != 7:
lst3 += [x,'A'] # This is just concatenates the list [x,'A'] with lst3
else:
lst3 += [x,'B']
print lst3 # [1, 'A', 2, 'A', 3, 'A', 4, 'A', 5, 'A', 6, 'A', 7, 'A', 8, 'B', 9, 'A']
Note that lst3 += [x, 'A'] could also be written as
lst3.append(x)
lst3.append('A')
Also, sum() is used with a generator expression and it's optional start argument.
Finally, enumerate returns a generator-like object that, when iterated over, produces a (index, value) tuple at each iteration -- see the docs I linked for a small example.

indexing and finding values in list of namedtuples

I have a namedtuple like the following,
tup = myTuple (
a=...,
b=...,
c=...,
)
where ... could be any value(string, number, date, time, etc). Now, i make a list of these namedtuples and want to find, lets say c=1 and the corresponding value of a and b. Is there any pythonic way of doing this?
Use List Comprehension, like a filter, like this
[[record.a, record.b] for record in records if record.c == 1]
For example,
>>> myTuple = namedtuple("Test", ['a', 'b', 'c', 'd'])
>>> records = [myTuple(3, 2, 1, 4), myTuple(5, 6, 7, 8)]
>>> [[record.a, record.b] for record in records if record.c == 1]
[[3, 2]]

Categories