How to generate list with repeating items? - python-3.x

Suppose I want to create a list like the following with a list comprehension:
["2", "2", "2", "3", "3", "3", "4", "4", "4"]
I have tried:
>>> [*[str(n)] * 3 for n in range(2, 5)]
File "<stdin>", line 1
SyntaxError: iterable unpacking cannot be used in comprehension
and
>>> [str(n) * 3 for n in range(2, 5)]
['222', '333', '444']
where I got the numbers, but together in one string, and
>>> [[str(n)] * 3 for n in range(2, 5)]
[['2', '2', '2'], ['3', '3', '3'], ['4', '4', '4']]
where I got a nested list, but want a flat one. Can this be done in a simple way, or do I have to take the nested list method and flatten the list ?

A nested for loop is your best bet. I think this is the simplest solution:
[str(n) for n in range(2, 5) for i in range(3)]

A simple way you could do this with list comprehension is by using integer division.
[str(n // 3) for n in range(6, 15)]

You can use the function chain function
from the itertools library in order to flatten the list.
>>> from itertools import chain
>>> list(chain(*[[str(n)] * 3 for n in range(2, 5)]))
['2', '2', '2', '3', '3', '3', '4', '4', '4']

Related

creating a list of lists from text file where the new list is based on a condition [duplicate]

This question already has answers here:
Sorting sub-lists into new sub-lists based on common first items
(4 answers)
Closed 2 years ago.
I have a text file that has lines in following order:
1 id:0 e1:"a" e2:"b"
0 id:0 e1:"4" e2:"c"
0 id:1 e1:"6" e2:"d"
2 id:2 e1:"8" e2:"f"
2 id:2 e1:"9" e2:"f"
2 id:2 e1:"d" e2:"k"
and I have to extract a list of lists containing elements (e1,e2) with id determining the index of the outer list and inner list following the order of the lines. So in the above case my output will be
[[("a","b"),("4","c")],[("6","d")],[("8","f"),("9","f"),("d","k")]]
The problem for me is that to know that the beginning of the new inner list, I need to check if the id value has changed. Each id does not have fixed number of elements. For example id:0 has 2, id:1 has 1 and id:2 has 3. Is there a efficient way to check this condition in next line while making the list?
You can use itertools.groupby() for the job:
import itertools
def split_by(
items,
key=None,
processing=None,
container=list):
for key_value, grouping in itertools.groupby(items, key):
if processing:
grouping = (processing(group) for group in grouping)
if container:
grouping = container(grouping)
yield grouping
to be called as:
from operator import itemgetter
list(split_by(items, itemgetter(0), itemgetter(slice(1, None))))
The items can be easily generated from text above (assuming it is contained in the file data.txt):
def get_items():
# with io.StringIO(text) as file_obj: # to read from `text`
with open(filename, 'r') as file_obj: # to read from `filename`
for line in file_obj:
if line.strip():
vals = line.replace('"', '').split()
yield tuple(val.split(':')[1] for val in vals[1:])
Finally, to test all the pieces (where open(filename, 'r') in get_items() is replaced by io.StringIO(text)):
import io
import itertools
from operator import itemgetter
text = """
1 id:0 e1:"a" e2:"b"
0 id:0 e1:"4" e2:"c"
0 id:1 e1:"6" e2:"d"
2 id:2 e1:"8" e2:"f"
2 id:2 e1:"9" e2:"f"
2 id:2 e1:"d" e2:"k"
""".strip()
print(list(split_by(get_items(), itemgetter(0), itemgetter(slice(1, None)))))
# [[('a', 'b'), ('4', 'c')], [('6', 'd')], [('8', 'f'), ('9', 'f'), ('d', 'k')]]
This efficiently iterates through the input without unnecessary memory allocation.
No other packages are required
Load and parse the file:
Beginning with a text file, formatted as shown in the question
# parse text file into dict
with open('test.txt', 'r') as f:
text = [line[2:].replace('"', '').strip().split() for line in f.readlines()] # clean each line and split it into a list
text = [[v.split(':') for v in t] for t in text] # split each value in the list into a list
d =[{v[0]: v[1] for v in t} for t in text] # convert liest to dicts
# text will appear as:
[[['id', '0'], ['e1', 'a'], ['e2', 'b']],
[['id', '0'], ['e1', '4'], ['e2', 'c']],
[['id', '1'], ['e1', '6'], ['e2', 'd']],
[['id', '2'], ['e1', '8'], ['e2', 'f']],
[['id', '2'], ['e1', '9'], ['e2', 'f']],
[['id', '2'], ['e1', 'd'], ['e2', 'k']]]
# d appears as:
[{'id': '0', 'e1': 'a', 'e2': 'b'},
{'id': '0', 'e1': '4', 'e2': 'c'},
{'id': '1', 'e1': '6', 'e2': 'd'},
{'id': '2', 'e1': '8', 'e2': 'f'},
{'id': '2', 'e1': '9', 'e2': 'f'},
{'id': '2', 'e1': 'd', 'e2': 'k'}]
Parse the list of dicts to expected output
Use .get to determine if a key exists, and return some specified value, None in this case, if the key is nonexistent.
dict.get defaults to None, so this method never raises a KeyError.
If None is a value in the dictionary, then change the default value returned by .get.
test.get(v[0], 'something here')
test = dict()
for r in d:
v = list(r.values())
if test.get(v[0]) == None:
test[v[0]] = [tuple(v[1:])]
else:
test[v[0]].append(tuple(v[1:]))
# test dict appears as:
{'0': [('a', 'b'), ('4', 'c')],
'1': [('6', 'd')],
'2': [('8', 'f'), ('9', 'f'), ('d', 'k')]}
# final output
final = list(test.values())
[[('a', 'b'), ('4', 'c')], [('6', 'd')], [('8', 'f'), ('9', 'f'), ('d', 'k')]]
Code Updated and reduced:
In this case, text is a list of lists, and there's no need to convert it to dict d, as above.
For each list t in text, index [0] is always the key, and index [1:] are the values.
with open('test.txt', 'r') as f:
text = [line[2:].replace('"', '').strip().split() for line in f.readlines()] # clean each line and split it into a list
text = [[v.split(':')[1] for v in t] for t in text] # list of list of only value at index 1
# text appears as:
[['0', 'a', 'b'],
['0', '4', 'c'],
['1', '6', 'd'],
['2', '8', 'f'],
['2', '9', 'f'],
['2', 'd', 'k']]
test = dict()
for t in text:
if test.get(t[0]) == None:
test[t[0]] = [tuple(t[1:])]
else:
test[t[0]].append(tuple(t[1:]))
final = list(test.values())
Using defaultdict
Will save a few lines of code
Using text as a list of lists from above
from collections import defaultdict as dd
test = dd(list)
for t in text:
test[t[0]].append(tuple(t[1:]))
final = list(test.values())

Making dictionary from 2 lists

I have following two lists:
list1 = ['17-Q2', '1.00', '17-Q3', '2.00', '17-Q4', '5.00', '18-Q1', '6.00']
list2 = ['17-Q2', '1', '17-Q3', '2', '17-Q4', '5', '18-Q1', '6']
I want a dictionary in the following way. Can I do that in Python?
result = [17-Q2: 1(1.00), 17-Q3: 2(2.00), 17-Q4: 5(5.00), 18-Q1: 6(6.00)]
Here's one approach to this:
result = {}
list1=['17-Q2', '1.00', '17-Q3', '2.00', '17-Q4', '5.00', '18-Q1', '6.00']
list2=['17-Q2', '1', '17-Q3', '2', '17-Q4', '5', '18-Q1', '6']
for i in range(0, len(list1)-1, 2):
result[list1[i]] = list2[i + 1] + '(' + list1[i+1] + ')' ;
You can zip the two lists and then zip the resulting iterable with itself so that you can iterate through it in pairs to construct the desired dict:
i = zip(list1, list2)
result = {k: '%s(%s)' % (v2, v1) for (k, _), (v1, v2) in zip(i, i)}
result becomes:
{'17-Q2': '1(1.00)', '17-Q3': '2(2.00)', '17-Q4': '5(5.00)', '18-Q1': '6(6.00)'}
You can use zip and dict.
keys = ["a", "b", "c", "d"]
values = ["A", "B", "C"]
print(dict(zip(keys, values)))
# prints: {'a': 'A', 'b': 'B', 'c': 'C'}
This works because dict can take a list (or any iterator) of tuples of (key, value) to be created. The zip function allows to build tuples from lists:
zip returns an iterator of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables.
Notice that this will only return a dictionary that pairs the shortest list (either keys or value) with the corresponding element.
If you wish to have a default value for unpaired elements you can always use itertools.zip_longest
from itertools import zip_longest
keys = ["a", "b", "c", "d"]
values = ["A", "B", "C"]
print(dict(zip_longest(keys, values)))
# prints: {'a': 'A', 'b': 'B', 'c': 'C', 'd': None}
You can also use zip_longest keyword parameter fillvalue to have something else than None when the corresponding value isn't found.
On the other hand, if you have more values than keys, this wouldn't make much sense as you would erase the default key (namely fillvalue) for every missing element.
Assuming your input to be as follows:
list1 = ['17-Q2', '1.00', '17-Q3', '2.00', '17-Q4', '5.00', '18-Q1', '6.00']
list2 = ['17-Q2', '1', '17-Q3', '2', '17-Q4', '5', '18-Q1', '6']
And Desired Output to be as follows:
result = {17-Q2: 1(1.00), 17-Q3: 2(2.00), 17-Q4: 5(5.00), 18-Q1: 6(6.00)}
Following code with a single while loop could help:
from collections import OrderedDict
final_dict = dict()
i = 0 # Initializing the counter
while (i<len(list1)):
# Updating the final dict
final_dict.update({list1[i]:str(list2[i+1]) + "(" + str(list1[i+1]) + ")"})
i += 2 # Incrementing by two in order to land on the alternative index
# If u want sorted dictionary
sorted_dict = OrderedDict(sorted(final_dict.items()))
print (sorted_dict)

Using a list comprehension, insert an incrementing integer into a list of lists that contain strings

How can I turn this for loop into a list comprehension?
in:
docs = [['a'], ['b']]
i=0
for each in docs:
print(each.insert(0, str(i)))
i+=1
print(docs)
out:
[['0', 'a'], ['1', 'b']]
It depends on if you want a new list or mutate the old list:
[l.insert(0,str(i)) for i,l in enumerate(docs)]
[l.insert(0,str(i)) or l for i,l in enumerate(docs)]
[[str(i)]+docs[i] for i in range(len(docs))]
This will mutate the old list, but only as a side effect, the 'returned' list is wrong:
>>> x = [['a'],['b']]
>>> [ l.insert(0,str(i)) for i,l in enumerate(x)]
[None, None]
>>> x
[['0', 'a'], ['1', 'b']]
This can be fixed:
>>> x = [['a'],['b']]
>>> [ l.insert(0,str(i)) or l for i,l in enumerate(x)]
[['0', 'a'], ['1', 'b']]
>>> x
[['0', 'a'], ['1', 'b']]
>>>
or one can generate a new list without mutating the old list:
>>> y = [['a'],['b']]
>>> [ [str(i)]+y[i] for i in range(len(y))]
[['0', 'a'], ['1', 'b']]
>>> y
[['a'], ['b']]

Input on dictionnary with a counter

I'm new at Python, and i need your help for this.
I have a user input like :
5 72 245 62
And i need to split this integers into a dictionary like this :
{1=5;2=72;3=245;4=62}
I tried something like :
sequence = dict(x ,input().split())
Where x is incrementing counter.
If your desired end result is a Python dictionary, then I think you're pretty close.
You can actually use a python builtin to achieve this called enumerate:
>>> values = input().split()
1 2 3 4
>>> values
['1', '2', '3', '4']
>>>
>>> sequence = dict(enumerate(values))
>>> sequence
{0: '1', 1: '2', 2: '3', 3: '4'}
enumerate just goes through any iterable (such as a list of strings) and outputs the index of each item and the item itself in a tuple:
>>> for x in enumerate(values):
... print(x)
...
(0, '1')
(1, '2')
(2, '3')
(3, '4')
You can then call dict on an iterable of tuples (which is what enumerate produces) in order to turn them into a dictionary.
Of course, enumerate, like most things is zero-indexed, so you can also pass in a starting number if you would like to start a 1:
>>> sequence = dict(enumerate(values, 1))
>>> sequence
{1: '1', 2: '2', 3: '3', 4: '4'}
The problem with what you have
Let's say, as above, we have a list of strings. In order to match up numbers with each string in the list, we need something like the following:
>>> dict([(1, '1'), (2, '2')...])
Notice that I am passing one argument to dict: a list of tuples where each item in the list looks like (1, '1') and I have one container holding all of them.
Your attempt was the following:
>>> sequence = dict(x ,input().split())
This is interpreted probably something like (guessing on the x):
>>> dict(1, ['1', '2', '3'])
Which produces the following Traceback:
>>> dict(1, [1, 2, 3])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: dict expected at most 1 arguments, got 2
You are passing two arguments to dict, not one, which is what it expects.
It expects some kind of container with a bunch of things in it where the first element of each thing is mapped to the second element, such as the following:
>>> [(1, '1'), (2, '2')]

Referencing a number inside a tuple

I want to arrange a list of tuples similar to the one bellow in descending order using the numbers:
data = [('ralph picked', ['nose', '4', 'apple', '30', 'winner', '3']),
('aaron popped', ['soda', '1', 'popcorn', '6', 'pill', '4',
'question', '29'])]
I would like to sort the nested list so that the outcome would look somewhat like:
data2 = [('ralph picked', ['apple', '30', 'nose', '4', 'winner', '3']),
('aaron popped', ['question', '29', 'popcorn', '6', 'pill', '4', 'soda', '1'])]
I am trying to use this code for this:
data2=[]
for k, v in data:
data2 = ((k, sorted(zip(data[::2], data[1::2]), key=lambda x: int(x[1]), reverse=True) ))
[value for pair in data2 for value in pair]
print(data2)
But I keep getting the error message:
TypeError: int() argument must be a string or a number, not 'tuple'
I tried to rearrange the int in key=lambda x: int(x[1]) to different things, but I kept getting the same message, I am very new to python, the syntax often gets me. Any ideas on how to solve this? I really thank you very much!
Rather than trying to do everything at once, let's give things names:
data = [('ralph picked', ['nose', '4', 'apple', '30', 'winner', '3']),
('aaron popped', ['soda', '1', 'popcorn', '6', 'pill', '4', 'question', '29'])]
data2 = []
for k, v in data:
new_list = sorted(zip(v[::2], v[1::2]), key=lambda x: int(x[1]), reverse=True)
flattened = [value for pair in new_list for value in pair]
new_tuple = (k, flattened)
data2.append(new_tuple)
produces
>>> print(data2)
[('ralph picked', ['apple', '30', 'nose', '4', 'winner', '3']),
('aaron popped', ['question', '29', 'popcorn', '6', 'pill', '4', 'soda', '1'])]
You need to distinguish between data and v -- you only want to sort v, and you need to store the result of the list comprehension, otherwise you're just building it and throwing it away.
When you're having trouble with the syntax, break everything apart into its pieces and print them to see what's going on. For example, you could decompose new_list into
words = v[::2]
numbers = v[1::2]
pairs = zip(words, numbers)
sorted_pairs = sorted(pairs, key=lambda x: int(x[1]), reverse=True)
and sorted_pairs is really what new_list is.

Resources