Converting simple tuple into dictionary - python

I have a even length tuple having elements like ('a','b','c','d','e','f') which I want to convert to dictionary having elements like ['a':'b', 'c':'d', 'e':'f'].
I tried using dict(tuple) but that wasn't helping. I have just started learning Python and any help will be highly appreciable.

It looks like you're trying to group the tuple into pairs, and then make a dict out of those pairs. There are two ways to do this.
The first is zipping slices:
zip(t[::2], t[1::2])
This is called an "extended slice", which is of the form start:stop:step. The first one is ::2, so it has the default start (the beginning of the tuple) and stop (the end of the tuple), and a step of 2, so it gets elements 0, 2, and 4. The second one is 1::2, so it's the same, but it starts at 1 instead of the default, so it gets elements 1, 3, and 5.
See the tutorial section on Lists for more details. (Of course you're using a tuple, not a list, but they both slice the same way.)
The second is zipping an iterator with itself:
i = iter(t)
zip(i, i)
Since the two references to i are both the same iterator, whenever you advance one, it advances both. So, the first one gets #0, then the second gets #1, then the first gets #2, the second #3, and so on.
See the tutorial section on Iterators for more details. Also see How grouper works, which explains a more general version of this idea (or at least tries to).
Either way, you get ('a', 'b'), then ('c', 'd'), then ('e', 'f'), so you can just pass that to dict:
dict(zip(t[::2], t[1::2]))
So, which one is better?
Slicing is probably easier to understand. It's also usually faster.
However, slicing doesn't work on arbitrary iterables, just sequences, it wastes memory on big inputs (you're essentially making a complete extra copy of the sequence), and it's a little harder to generalize.
You should learn how both of them work so you can choose appropriately.

You can use a dict comprehension:
t = ('a','b','c','d','e','f')
d = {t[i]:t[i+1] for i in range(0,len(t),2)}
Note that the part
range(0,len(t),2)
will generate a list of the form
[0, 2, 4]

Try this:
t = ('a','b','c','d','e','f')
dict(t[i:i+2] for i in xrange(0, len(t), 2))
=> {'a': 'b', 'c': 'd', 'e': 'f'}

>>> tup = ('a','b','c','d','e','f')
>>> dct = dict(zip(tup[::2], tup[1::2]))
{'a': 'b', 'c': 'd', 'e', 'f'}
This should do the trick

def tup2dict():
tup = ('a','b','c','d','e','f')
print ({i:j for (i,j) in zip(tup,tup[1:])[::2]})
Thanks to iterating-over-every-two-elements-in-a-list and python-dictionary-comprehensionn.

Related

How to solve this task with lists?

Making a list from mainlist and from subslists
As chepner said, try write your own code without list comprehension and then modify your own code.
def pyramid(base, char):
return [ ['']*num_empty + [char]*num_char + ['']*num_empty for num_empty, num_char in enumerate(list(range(base, 0, -2)))][::-1]
I'd recommend breaking up the problems into two steps:
Generate the list of pyramid bricks (without the empty spaces)
Add in the empty spaces (as needed) to each element of that list
As for Step 1, you can easily accomplish it with this function:
def pyramid(base, char):
result = [[char] * i for i in range(1, base+1, 2)]
return result
Do you see what it's doing? It's looping through a range of odd numbers, and for each number, it is constructing a list of chars. Each constructed list will be an element of the returned result list.
So if you call pyramid(5, 'A'), you'll get:
[['A'], ['A', 'A', 'A'], ['A', 'A', 'A', 'A', 'A']]
This solution does not account for the empty spaces, however. To handle those empty spaces, you could either:
Run the result through a second list comprehension, or:
Edit the first (and only) list comprehension to include the proper number of spaces at the beginning and end of each sub-list.
I'll let you decide how to implement this for yourself. I hope this helps!

How to arrange the output of set based on predefined list

list1=['f','l','a','m','e','s'] #This is the predefined list
list2=['e','e','f','a','s','a'] #This is the list with repitition
x=list(set(list2)) # I want to remove duplicates
print(x)
Here I want the variable x to retain the order which list1 has. For example, if at one instance set(list2) produces the output as ['e','f','a','s'], I want it to produce ['f','a','e','s'] (Just by following the order of list1).
Can anyone help me with this?
Construct a dictionary that maps characters to their position in list1. Use its get method as the sort-key.
>>> dict1 = dict(zip(list1, range(len(list1))))
>>> sorted(set(list2), key=dict1.get)
['f', 'a', 'e', 's']
This is one way using dictionary:
list1=['f','l','a','m','e','s'] #This is the predefined list
list2=['e','e','f','a','s','a'] #This is the list with repitition
x=list(set(list2)) # I want to remove duplicates
d = {key:value for value, key in enumerate(list1)}
x.sort(key=d.get)
print(x)
# ['f', 'a', 'e', 's']
Method index from the list class can do the job:
sorted(set(list2), key=list1.index)
What is best usually depends on actual use. With this problem it is important to know the expected sizes of the lists to choose the most efficient approach. If we are keeping much of the dictionary the following query works well and has the additional benefit that it is easy to read.
set2 = set(list2)
x = [i for i in list1 if i in set2]
It would also work without turning list2 into a set first. However, this would run much slower with a large list2.

Iterable unpacking cannot be used in comprehension (Python 3 `*` operator) [duplicate]

Suppose I have a list of sets and I want to get the union over all sets in that list. Is there any way to do this using a generator expression? In other words, how can I create the union over all sets in that list directly as a frozenset?
Just use the .union() method.
>>> l = [set([1,2,3]), set([4,5,6]), set([1,4,9])]
>>> frozenset().union(*l)
frozenset([1, 2, 3, 4, 5, 6, 9])
This works for any iterable of iterables.
I assume that what you're trying to avoid is the intermediate creations of frozenset objects as you're building up the union?
Here's one way to do it. NOTE: this originally used itertools.chain() but, as Kenny's comment notes, the version below is slightly better:
import itertools
def mkunion(*args):
return frozenset(itertools.chain.from_iterable(args))
Invoke like this:
a = set(['a','b','c'])
b = set(['a','e','f'])
c = mkunion(a,b) # => frozenset(['a', 'c', 'b', 'e', 'f'])
Nested generator expression. But I think they are a bit cryptic, so the way KennyTM suggested may be clearer.
frozenset(some_item for some_set in some_sets for some_item in some_set)

Replacing a single element in a tuple nested within a list - Is their a better way?

Edit - I want to change the value of a tuple nested in a list, at a specific position
eg changed nestedTuple[1][1] change to 'xXXXXx'
I have come up with this code, that works, but it just seems very 'Un-pure!'
Convert to a list - change - convert to tuple - insert back into list
I ASSuME that it would be very demanding on resources.
Could anyone please advise me if their is a better way?
>>> nestedTuple= [('a','b','c'), ('d','e','f'), ('g','h','i')]
>>> tempList = list(nestedTuple[1])
>>> tempList[1] = 'xXXXXx'
>>> nestedTuple[1] = tuple(tempList)
>>> print nestedTuple
[('a', 'b', 'c'), ('d', 'xXXXXx', 'f'), ('g', 'h', 'i')]
You can use slicing.
>>> i = 1
>>> nestedTuple = [('a','b','c'), ('d','e','f'), ('g','h','i')]
>>> nestedTuple[1] = nestedTuple[1][:i] + ('xXXXXx', ) + nestedTuple[1][i+1:]
>>> nestedTuple
[('a', 'b', 'c'), ('d', 'xXXXXx', 'f'), ('g', 'h', 'i')]
How about this?
nested_tuple[1] = tuple('XXXXX' if i==1 else x for i, x in enumerate(nested_tuple[1]))
Note that tuples aren't meant to be changed, so one liners aren't going to be very clean.
Depending on how many changes you want to make in your nestedTuple and depending on downstream in your program. You may want to built a nestedList from your nestedTuple
nestedList = [list(myTuple) for myTuple in nestedTuple]
and then do:
nestedList[x][y] = 'truc'
and then make a new nestedTuple if needed
Otherwise you should profile this
I understand that this is data structure that you are getting. Performance aside it would make for much cleaner and readable code to change the data to nested list, do the manipulation, and if you need to write it back to convert it back to nested tuple. May be suboptimal in terms of speed, but that might not be the limiting factor for your application.
nestedTuple= [('a','b','c'), ('d','e','f'), ('g','h','i')]
nestedList = [list(x) for x in nestedTuple]
now you can use normal list slicing and assigning
nestedList[1][1] = ['xxxxXXxxx']
if you need the data back in original nested tuple format use the one liner:
nestedTuple = [tuple(x) for x in nestedList]
most readable and least likely to contain bugs if your data structure grows and your slicing becomes more complex.
Very purpose to use tuples is its immutable means once the tuple is created the values cannot be changed. In your case the best way will be to use nested lists i.e., as shown below
>>> nestedList = [['a','b','c'], ['d','e','f'], ['g','h','i']]
Now to change the element 'e' in the list to 'xxxx' you can use as shown below
>>> nestedList[1][1] = 'xxxx'

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

Categories

Resources