Taking elements two by two from lists - python

I have lists like:
['a', '2', 'b', '1', 'c', '4']
['d', '5', 'e', '7', 'f', '4', 'g', '6']
And I want to make a dictionary consist of keys as letters and values as numbers. I mean:
{'a': 2, 'b': 1, 'c': 4, 'd':5, 'e':7, 'f':4, 'g':6}

You can try:
>>> l = ['a', '2', 'b', '1', 'c', '4']
>>> it = iter(l)
>>> dict(zip(it, it))
{'a': '2', 'c': '4', 'b': '1'}
First you create an iterator out of the list. Then with zip of the iterator with itself you take pair of values from the list. Finally, with dict you transform these tuples to your wanted dictionary.
If you also want to do the string to number conversion, then use:
{x: int(y) for x, y in zip(it, it)}
EDIT
If you don't want to use zip then:
{x: int(next(it)) for x in it}

l = ['a', '2', 'b', '1', 'c', '4']
d = {k:v for k,v in zip(l[::2], l[1::2])}
Or if you want the numbers to be actual numbers:
l = ['a', '2', 'b', '1', 'c', '4']
d = {k:int(v) for k,v in zip(l[::2], l[1::2])}
Use float(v) instead of int(v) if the numbers have the potential to be floating-point values instead of whole numbers.
Without using any built-in functions:
l = ['a', '2', 'b', '1', 'c', '4']
d = {}
l1 = l[::2]
l2 = l[1::2]
idx = 0
while 1:
try:
d[l1[idx]] = l2[idx]
idx += 1
except IndexError:
break

You can split the lists into two, one containing the letters and the other containing the keys, with
key_list = old_list[::2]
value_list = old_list[1::2]
Then you can loop over the two lists at once with zip and you can make the dictionary.

Related

Switching between min and max by user inter action

I have the following code which allows the user to choose between min and max filtering. However, min works on element 1 but max works on element 2. Is there a better way to implement it?
from operator import itemgetter
data = [['A', '2', '4'], ['B', '2', '12'],
['C', '3', '88'], ['D', '4', '88']]
fn_max = False
if fn_max is True:
mx = max(data, key=itemgetter(2))[2]
mx_values = [d for d in data if d[2] == mx]
else:
mx = min(data, key=itemgetter(1))[1]
mx_values = [d for d in data if d[1] == mx]
print(mx_values)
You can put them into a function:
from operator import itemgetter
data = [['A', '2', '4'], ['B', '2', '12'],
['C', '3', '88'], ['D', '4', '88']]
def my_func(fn_max):
func, i = (max, 2) if fn_max else (min, 1)
mx = func(data, key=itemgetter(i))[i]
return [d for d in data if d[i] == mx]
fn_max = False
print(my_func(fn_max))
Output:
[['A', '2', '4'], ['B', '2', '12']]
This is arguably better (although it's unclear what you need it for, so it's hard to say if it would really be better):
data = [
['A', 2, 4],
['B', 2, 12],
['C', 3, 88],
['D', 4, 88]
]
def fn_filter(fn, column, data):
value = fn(rec[column] for rec in data)
return list(filter(lambda rec: rec[column] == value, data))
print(fn_filter(min, 1, data))
print(fn_filter(max, 2, data))
Result:
[['A', 2, 4], ['B', 2, 12]]
[['C', 3, 88], ['D', 4, 88]]
fn_filter allows you to apply any function fn to a specific column column of the dataset data and will return a list of all the records in data that have that same value in that same column.

How to combine list of lists into a dictionary where the first element of a nested list is the key

I'm trying to combine a list, similar to the following
l = [['A', '1'], ['A', '2'], ['B', '1'], ['C', '1'], ['C', '2']]
into a dictionary, where the first element of a nested list is the key
d = {'A': ['1', '2'], 'B': ['1'], 'C': ['1', '2']}
Is it possible to do this fairly easily?
Simplest is the following, using dict.setdefault:
d = {}
for k, v in l:
d.setdefault(k, []).append(v)
With a collections.defaultdict, the code gets even cleaner:
from collections import defaultdict
d = defaultdict(list)
for k, v in l:
d[k].append(v)
You can use itertools.groupby:
l = [['A', '1'], ['A', '2'], ['B', '1'], ['C', '1'], ['C', '2']]
d={}
for i,k in itertools.groupby(sorted(l, key=lambda x: x[0]), key=lambda x: x[0]):
d[i]=list(itertools.chain(*(p[1:] for p in k)))
>>> print(d)
{'A': ['1', '2'], 'B': ['1'], 'C': ['1', '2']}
Here is a traditional way of doing it. Just iterate over the list and append the keys and values to the dictionary. Here is the full code:
l = [['A', '1'], ['A', '2'], ['B', '1'], ['C', '1'], ['C', '2']]
dictionary = {}
for key,value in l:
if key in dictionary.keys():
dictionary[key].append(value)
else:
dictionary[key] = [value]
print(dictionary)
Output:
{'A': ['1', '2'], 'B': ['1'], 'C': ['1', '2']}
Using defaultdict can help here
from collections import defaultdict
l = [['A', '1'], ['A', '2'], ['B', '1'], ['C', '1'], ['C', '2']]
d = defaultdict(list)
for x in l:
d[x[0]].append(x[1])
print(d)
output
defaultdict(<class 'list'>, {'A': ['1', '2'], 'B': ['1'], 'C': ['1', '2']})
Iterate over l and append in the keys of the dictionbary like this:
d = {}
for item in l:
if item[0] in d.keys():
d[item[0]].append(item[1])
if item[0] not in d.keys():
d[item[0]] = [item[1]]

Create a dictionary by zipping together two lists of uneven length [duplicate]

This question already has answers here:
How to zip two differently sized lists, repeating the shorter list?
(15 answers)
Closed 4 years ago.
I have two lists different lengths, L1 and L2. L1 is longer than L2. I would like to get a dictionary with members of L1 as keys and members of L2 as values.
As soon as all the members of L2 are used up. I would like to start over and begin again with L2[0].
L1 = ['A', 'B', 'C', 'D', 'E']
L2 = ['1', '2', '3']
D = dict(zip(L1, L2))
print(D)
As expected, the output is this:
{'A': '1', 'B': '2', 'C': '3'}
What I would like to achieve is the following:
{'A': '1', 'B': '2', 'C': '3', 'D': '1', 'E': '2'}
Use itertools.cycle to cycle around to the beginning of L2:
from itertools import cycle
dict(zip(L1, cycle(L2)))
# {'A': '1', 'B': '2', 'C': '3', 'D': '1', 'E': '2'}
In your case, concatenating L2 with itself also works.
# dict(zip(L1, L2 * 2))
dict(zip(L1, L2 + L2))
# {'A': '1', 'B': '2', 'C': '3', 'D': '1', 'E': '2'}
Use itertools.cycle:
from itertools import cycle
L1 = ['A', 'B', 'C', 'D', 'E']
L2 = ['1', '2', '3']
result = dict(zip(L1, cycle(L2)))
print(result)
Output
{'E': '2', 'B': '2', 'A': '1', 'D': '1', 'C': '3'}
As an alternative you could use enumerate and index L2 modulo the length of L2:
result = {v: L2[i % len(L2)] for i, v in enumerate(L1)}
print(result)
cycle is fine, but I shall add this modulo based approach:
{x: L2[i % len(L2)] for i, x in enumerate(L1)]}
You can also use a collections.deque() to create an circular FIFO queue:
from collections import deque
L1 = ['A', 'B', 'C', 'D', 'E']
L2 = deque(['1', '2', '3'])
result = {}
for letter in L1:
number = L2.popleft()
result[letter] = number
L2.append(number)
print(result)
# {'A': '1', 'B': '2', 'C': '3', 'D': '1', 'E': '2'}
Which pops the left most item currently in L2 and appends it to the end once the number is added to the dictionary.
Note: Both collections.deque.popleft() and collections.deque.append() are O(1) operations, so the above is still O(N), since you need to traverse all the elements in L1.
Other option without dependencies with good old for loop:
D = {}
for i, e in enumerate(L1):
D[e] = L2[i%len(L2)]
D #=> {'A': '1', 'B': '2', 'C': '3', 'D': '1', 'E': '2'}
Or just:
{ e: L2[i%len(L2)] for i, e in enumerate(L1) }
#=> {'A': '1', 'B': '2', 'C': '3', 'D': '1', 'E': '2'}

Get a dict from a python list

I have a list my_list = ['a', 'b', 'c', 'd'] and I need to create a dictionary which looks like
{ 'a': ['a', 'b', 'c', 'd'],
'b': ['b', 'a', 'c', 'd'],
'c': ['c', 'a', 'b', 'd'],
'd': ['d', 'a', 'b', 'c'] }
each element as its value being the list but the first element is itself.
Here is my code
my_list = ['1', '2', '3', '4']
my_dict=dict()
for x in my_list:
n = my_lst[:]
n.remove(x)
n= [x] + n[:]
my_dict[x] = n
print my_dict
which gives
{'1': ['1', '2', '3', '4'],
'3': ['3', '1', '2', '4'],
'2': ['2', '1', '3', '4'],
'4': ['4', '1', '2', '3']}
as required.
But I don't think that's the most optimal way of doing it. Any help to optimize will be appreciated.
>>> seq
['a', 'b', 'c', 'd']
>>> {e: [e]+[i for i in seq if i != e] for e in seq}
{'a': ['a', 'b', 'c', 'd'],
'b': ['b', 'a', 'c', 'd'],
'c': ['c', 'a', 'b', 'd'],
'd': ['d', 'a', 'b', 'c']}
A faster approach (than the accepted answer) for larger lists is
{e: [e] + seq[:i] + seq[i+1:] for i, e in enumerate(seq)}
Relative timings:
In [1]: seq = list(range(1000))
In [2]: %timeit {e: [e]+[i for i in seq if i != e] for e in seq}
10 loops, best of 3: 40.8 ms per loop
In [3]: %timeit {e: [e] + seq[:i] + seq[i+1:] for i, e in enumerate(seq)}
100 loops, best of 3: 6.03 ms per loop
You can get hacky with a dictionary comprehension:
my_dict = {elem: list(sorted(my_list, key=lambda x: x != elem)) for elem in my_lst}
This works on the fact that the sorted function performs a stable sort, and False is less than True
Edit: This method is less clear and probably slower, use with caution.

How can I rearrange this alphabetically?

I want to rearrange a file alphabetically. Also I want the number to be printed next to the arranged letter.
e.g.:
a 4
c 5
e 6
f 2
here is my code:
f = open("1.txt","r")
r = f.read()
print(r)
r=r.split()
line=sorted(r)
for row in line:
print(line)
and here are the results I'm getting:
f 2
c 5
e 6
a 4
['2', '4', '5', '6', 'a', 'c', 'e', 'f']
['2', '4', '5', '6', 'a', 'c', 'e', 'f']
['2', '4', '5', '6', 'a', 'c', 'e', 'f']
['2', '4', '5', '6', 'a', 'c', 'e', 'f']
['2', '4', '5', '6', 'a', 'c', 'e', 'f']
['2', '4', '5', '6', 'a', 'c', 'e', 'f']
['2', '4', '5', '6', 'a', 'c', 'e', 'f']
['2', '4', '5', '6', 'a', 'c', 'e', 'f']
>>>
To get the pairs in sublists map str.split on the file object and call sorted on that:
with open("in.txt") as f:
print(sorted(map(str.split,f)))
in.txt:
e 6
c 5
f 2
a 4
Output:
[['a', '4'], ['c', '5'], ['e', '6'], ['f', '2']]
To sort a file alphabetically just getting the lines you would simply call sorted on the file object:
with open("test.txt") as f:
print(sorted(f))
If you want to format the output:
with open("test.txt") as f:
for sub in sorted(map(str.split,f)):
print("letter = {}, num = {}".format(*sub))
letter = a, num = 4
letter = c, num = 5
letter = e, num = 6
letter = f, num = 2
Also why you see ['2', '4', '5', '6', 'a', 'c', 'e', 'f'] is because calling split on .read splits all the data into a single list as split, splits on any whitespaces and when lexicographically comparing string digits to letters digits are considered lower so 2 < a, also beware when you are comparing string digits against each other as 11 > 100 = True as strings are compared character by character as 1 is considered greater than 0 100 would appear before 11 in your sorted list, when comparing digits cast to int.
If you want to have a max of three scores per user always keeping the most recent, you can use a deque with a maxlen of 3 and after the initial sort pickle the dict.
from csv import reader
from collections import deque, OrderedDict
import pickle
name, new_score = "foo",100
with open("test.txt") as f:
d = OrderedDict((name, deque(map(int,rest),maxlen=3)) for name, *rest in reader(sorted(f)))
print(d)
d[name].append(new_score)
print(d)
with open("my_data.pkl","wb") as out:
pickle.dump(d, out)
with open("my_data.pkl","rb") as out:
print(pickle.load(out))
test.txt:
homer,2
foo,1,2,3
bar,4,5,6
Output:
OrderedDict([('bar', deque([4, 5, 6], maxlen=3)), ('foo', deque([1, 2, 3], maxlen=3)), ('homer', deque([2], maxlen=3))])
OrderedDict([('bar', deque([4, 5, 6], maxlen=3)), ('foo', deque([2, 3, 100], maxlen=3)), ('homer', deque([2], maxlen=3))])
OrderedDict([('bar', deque([4, 5, 6], maxlen=3)), ('foo', deque([2, 3, 100], maxlen=3)), ('homer', deque([2], maxlen=3))])
Once sorted you just need to load to get the dict and dump after you have written.
You need to use readlines() instead of read() to get each line of the file as a seperate element of the list. Then a simple sort of the list will work.
f = open('1.txt','r')
# Use readlines isntead of of read to get an list of lines
lines = f.readlines()
print ''.join(lines)
# Now sort the list (default is by first letter
lines.sort()
print ''.join(lines)
Alternatively you could force the split function to use the end of line char '\n' instead of the default which is all white space. But now you will need to join the list back with the new line char ('\n') instead of an empty string.
f = open('1.txt','r')
lines = f.read()
lines = lines.split('\n')
print '\n'.join(lines)
# Now sort the list (default is by first letter
lines.sort()
print '\n'.join(lines)

Categories

Resources