Adding spaces to duplicate elements in list - python

I have the following list1:
['Tom', 'Michael', 'Tom', 'Tom']
I would like to add a space to duplicate values in order to obtain the following ouput:
['Tom', 'Michael', 'Tom ', 'Tom ']
As Tom is repeated 2 times the 1st it has I space and the second 2 spaces at the end
I was thing in using a line of code like this
[ f'{x} ' for x in list1 if .... ]
But not really sure how could I apply it for my case
This is what I tried so far:
from collections import Counter
d = Counter(list1)
res = [k for k, v in d.items() if v > 1]
print([ f'{x} ' for x in res ])
Is there a more efficient way to do it including the addition of spaces?

because you want to add spaces as the number of occurences until each element, you can use count method of lists. Then multiply this count with " ":
l = ['Tom', 'Michael', 'Tom', 'Tom']
res = []
for i in range(len(l)):
cnt = l[:i].count(l[i])
res.append(l[i] + " "*cnt)
Or in one line:
res = [l[i] + " " * l[:i].count(l[i]) for i in range(len(l))]
And both give as expected:
['Tom', 'Michael', 'Tom ', 'Tom ']
Another different approach can be to use defaultdict and "remember" the counts in one go (O(n)), instead of counting for each element (O(n^2)):
from collections import defaultdict
l = ['Tom', 'Michael', 'Tom', 'Tom']
res = []
d = defaultdict(int)
for x in l:
cnt = d[x]
res.append(x + " "*cnt)
d[x] = cnt + 1
This way if an element is seen for the first time its count will be initiazized to 0.
Or, lastly, without any imports. We can just use a regular dictionary and take advantage of the setdefault method:
l = ['Tom', 'Michael', 'Tom', 'Tom']
res = []
d = {}
for x in l:
res.append(x + " "*d.setdefault(x, 0))
d[x] += 1

This gives the output that you want:
x = ['Tom', 'Michael', 'Tom', 'Tom']
z=[]
for y in range(len(x)):
z.append(x[y] + " "*x[:y].count(x[y]))
This yields a z of ['Tom', 'Michael', 'Tom ', 'Tom ']
As a list comprehension, this looks like:
[x[y] + " "*x[:y].count(x[y]) for y in range(len(x))]

This seems like a good opportunity to use a Counter collection, like this:
from collections import Counter
lst = ['Tom', 'Michael', 'Tom', 'Tom']
nums = Counter()
ans = []
for name in lst:
ans.append(name + ' ' * nums[name])
nums[name] += 1
The trick is to keep track of the number of occurrences of the name using the Counter, and update it as we go. Notice that the ' ' * nums[name] part simply says: "print this number of spaces". It works as expected:
ans
=> ['Tom', 'Michael', 'Tom ', 'Tom ']

Here is a solution that avoids repeatedly using count on the original list.
We build the output list, keeping track with a collections.Counter of the previously encountered items:
from collections import Counter
data = ['Tom', 'Michael', 'Tom', 'Tom']
out = []
counts = Counter()
for item in data:
out.append(item + ' '*counts[item])
counts.update((item,))
print(out)
# ['Tom', 'Michael', 'Tom ', 'Tom ']
Note the comma in counts.update((item,)): as update expects an iterable, we pass it a tuple containing just one element, our item. Passing directly the string would count the individual letters.

Related

how to add/merge values together in a python dictionary

I have two list
alist = [1,2,5,3,7,3,21,7,2]
blist = [mary, tom, ken, mary, tom, peter, joseph, mary, ken]
in the end I would like to have a python dictionary:
{"mary": 11, "tom": 9, "ken": 7, "peter": 7, "joseph":21}
adding all their marks together according to their names.
I tried something like this:
for (marks, name) in zip(alist,blist):
dict[name] += marks
I have this solution:
d = dict()
for (marks, name) in zip(alist,blist):
if name in d:
d[name] += marks
else:
d[name] = marks
Maybe something more efficient could be written but I think this works.
You can use collections.Counter, it is probably the most efficient way of doing this.
from collections import Counter
c = Counter()
for mark, name in zip(alist, blist):
c[name] += mark
print(c)
Output:
Counter({'joseph': 21, 'mary': 11, 'tom': 9, 'ken': 7, 'peter': 3})
Counter works just like a dictionary, but extends it with some additional methods like:
print(c.most_common(3))
Output:
[('joseph', 21), ('mary', 11), ('tom', 9)]
zip(*iterables)
Make an iterator that aggregates elements from each of the iterables.
https://docs.python.org/3.3/library/functions.html#zip
from collections import defaultdict
alist = [1,2,5,3,7,3,21,7,2]
blist = ['mary', 'tom', 'ken', 'mary', 'tom', 'peter', 'joseph', 'mary', 'ken']
data_dict = defaultdict(int)
for i, count in zip(blist, alist):
data_dict[i] += count
You can use this solution alternatively
alist = [1,2,5,3,7,3,21,7,2]
blist = ['mary', 'tom', 'ken', 'mary', 'tom', 'peter', 'joseph', 'mary', 'ken']
my_dict = {}
for key, value in zip(blist, alist):
my_dict[key] = my_dict.get(key, 0) + value
dict(zip(alist, blist))
This may help taken from this article https://www.geeksforgeeks.org/python-convert-two-lists-into-a-dictionary/amp/

Why am I not getting the correct number of objects in my list?

I'm trying to create a function to generate a list of tuples. I have this:
import random
def generate_list(count):
l_names = ['scott', 'anderson', 'philips', 'peterson', 'parker']
f_names = ['james', 'chris', 'lisa', 'mary', 'kate']
names = []
counter = 0
for name in f_names:
counter += 1
my_tuple = (counter, f_names[random.randint(0, len(f_names)-1)], \
l_names[random.randint(0, len(l_names)-1)])
names.append(my_tuple)
return my_tuple
people = generate_list(3)
print(f"People list: {people}")
When I use generate_list(3), I want three tuples in my list. But I'm only getting one. I feel like the mistake is somewhere in the for loop and its return statement. But I can't figure it out.
Can anyone help?
Thanks
Change for name in f_names to for name in range(count) and return mytuple to return names, since you are returning one tuple not the list of names.
import random
def generate_list(count):
l_names = ['scott', 'anderson', 'philips', 'peterson', 'parker']
f_names = ['james', 'chris', 'lisa', 'mary', 'kate']
names = []
counter = 0
for name in range(count): #change f_names to count
counter += 1
my_tuple = (counter, random.choice(f_names), \
random.choice(l_names))
names.append(my_tuple)
return names #return names not my_tuples
people = generate_list(3)
print(f"People list: {people}")
import random
def generate_list(count):
l_names = ['scott', 'anderson', 'philips', 'peterson', 'parker']
f_names = ['james', 'chris', 'lisa', 'mary', 'kate']
names = []
counter = 0
while counter < count:
counter += 1
my_tuple = (counter, f_names[random.randint(0, len(f_names)-1)], \
l_names[random.randint(0, len(l_names)-1)])
names.append(my_tuple)
return names
people = generate_list(3)
print(f"People list: {people}")
The idea was correct however the for loop you created went on through all of the names of f_names which wasn't the thing you were looking for. So you can change the for loop to a simple while loop so it will stop when it reached the count which was given in the function.

Taking two lists as input that contain words, to form a tuple with two words, one from each list that have the same starting letter of each word

I've to take two lists as an input which contain words. Using those words I form a tuple using two words, one from each list that contain the same first letter of each word. Then creating a list of those tuples and printing them.
I have a solution, however, I cannot seem to produce the same item twice. Here's an example of what I mean in words.
List A: ['Jack', 'Tim', 'John', 'Ahmed']
List B: ['Julie', 'Tom', 'Henry', 'Harper']
c = input().lower()
d = input().lower()
a = c.split()
b = d.split()
x = []
for i in range(len(a)):
if a[i][0] == b[i][0]:
x.append((a[i], b[i]))
print(x)
My Output: [('joy', 'juggle'), ('troy', 'trim')]
Expected Output: [('Jack', 'Julie'), ('John', 'Julie'), ('Tim', 'Tom')]
I'm quite new to learning the language and wasn't quite able to find any similarities to my previous work to find out how I could reiterate through a / b without reproducing the same output.
Use itertools.product to get all the pairs and then filter them out:
In [1]: from itertools import product
In [2]: a = ['Jack', 'Tim', 'John', 'Ahmed']
In [3]: b = ['Julie', 'Tom', 'Henry', 'Harper']
In [4]: out = [i for i in product(a, b) if i[0][0] == i[1][0]]
In [5]: out
Out[5]: [('Jack', 'Julie'), ('Tim', 'Tom'), ('John', 'Julie')]
With list comprehensions:
In [50]: a = ['Jack', 'Tim', 'John', 'Ahmed']
In [51]: b = ['Julie', 'Tom', 'Henry', 'Harper']
In [55]: c = [(x,y) for x in a for y in b if x.lower()[0]==y.lower()[0]]
In [56]: c
Out[56]: [('Jack', 'Julie'), ('Tim', 'Tom'), ('John', 'Julie')]
You can try this
[(x, y) for x in A for y in B if x[0] == y[0]]

Appending a list in a dictionary with a value of index of a list

I have a dictionary and a list:
results = {"Alice":[], "Bob":[], "Clare":[], "Dennis":[], "Eva":[]}
list_of_names = ['Bob', 'Alice', 'Clare', 'Eva', 'Dennis']
and I want to fill those lists with a value of index+1 accordingly.
So that if we have the list above the dictionary would look like this
results = {"Alice":[2], "Bob":[1], "Clare":[3], "Dennis":[5], "Eva":[4]}
this is my current code
Aindex = list_of_names.index("Alice")
Bindex = list_of_names.index("Bob")
Cindex = list_of_names.index("Clare")
Dindex = list_of_names.index("Dennis")
Eindex = list_of_names.index("Eva")
Aindex = Aindex + 1
Bindex = Bindex + 1
Cindex = Cindex + 1
Dindex = Dindex + 1
Eindex = Eindex + 1
results["Alice"].append(Aindex)
results["Bob"].append(Bindex)
results["Clare"].append(Cindex)
results["Dennis"].append(Dindex)
results["Eva"].append(Eindex)
Is there any way to shorten this code and make it work for any amount of dictionary/list entries?
yes. One line with dictionary comprehension and enumerate starting at 1:
list_of_names = ['Bob', 'Alice', 'Clare', 'Eva', 'Dennis']
results = {name:[i] for i,name in enumerate(list_of_names,1)}
>>> results
{'Alice': [2], 'Bob': [1], 'Clare': [3], 'Dennis': [5], 'Eva': [4]}
If you want to use the existing list inside the dict
results = {"Alice":[], "Bob":[], "Clare":[], "Dennis":[], "Eva":[]}
list_of_names = ['Bob', 'Alice', 'Clare', 'Eva', 'Dennis']
for i,j in enumerate(list_of_names,1):
results[j].append(i)

count how many a combination occurs in a list

I created a list by doing this:
list3= [zip(Indiener1, Indiener2)]
Both elements are long lists of names.
But as a third element in the small combined list I want the number of times the combination of names occurs in the whole list3 as I have to do calculations with that number.
I tried list3.count() but that function only wanted to take one item.
How can I do this?
from collections import Counter
list1=["a","b","d","b"]
list2=["5","u","55","u"]
list3=zip(list1,list2)
print Counter(list3)
it outputs:
Counter({('b', 'u'): 2, ('d', '55'): 1, ('a', '5'): 1})
Use a counter and reverse the pairings to get ("foo","bar") == ("bar","foo"):
l1 =["foo","bar","foobar"]
l2 = ["bar","foo","bar"]
from collections import Counter
c = Counter(zip(l1,l2))
for k,v in c.items():
rev = tuple(reversed(k))
print("pairing {} appears {}".format(k,v + c.get(rev,0)))
To avoid getting double output ('foo', 'bar') and ('bar', 'foo') you can add rev to a set and check that it has not been seen already:
from collections import Counter
c = Counter(zip(l1,l2))
seen = set()
for k, v in c.items():
rev = tuple(reversed(k))
if k not in seen:
seen.add(rev)
print("pairing {} appears {} times".format(k,v + c.get(rev,0)))
pairing ('foo', 'bar') appears 2 times
pairing ('foobar', 'bar') appears 1 times
Since ("foo","bar") and ("bar","foo") are considered the same, you have to count on something like sets, where order doesn't matter:
>>> from collections import Counter
>>> l1 = ['John', 'Doe', 'Paul', 'Pablo', 'Paul', 'Doe']
>>> l2 = ['Doe', 'John', 'Doe', 'Doe', 'Doe', 'Paul']
>>> print Counter(frozenset(pair) for pair in zip(l1, l2))
Counter({
frozenset(['Paul', 'Doe']): 3,
frozenset(['John', 'Doe']): 2,
frozenset(['Doe', 'Pablo']): 1
})
You can also sort the pairs before counting, but a set makes the purpose more explicit.

Categories