Python3 toggle between two strings - string

Is there a more elegant way to toggle between two strings or integers in Python 3?
x={"A":"B", "B":"A"}[x]
The values can be non-boolean, like string or integer.
Let's suppose that we want to toggle between "A" and "B" and the variable name is x.
In other words: if x = "A" then the result should be x = "B" and if x = "B" then the result should be x = "A".
input:
x="B"
output:
x="A"

Using a dict is already pretty smart. Here is an alternative:
x = 'B' if x == 'A' else 'A'

You can write something like that:
def toggle(x):
x['A'], x['B'] = x['B'], x['A']
x = {'A': 'B', 'B': 'A'}
or that:
def toggle(x):
x.update(dict(zip(x.keys(), list(x.values())[::-1])))
x = {'A': 'B', 'B': 'A'}
print(x)
toggle(x)
print(x)
toggle(x)
print(x)
OUTPUT:
{'A': 'B', 'B': 'A'}
{'A': 'A', 'B': 'B'}
{'A': 'B', 'B': 'A'}

Related

Python Groupby keys list of dictionaries

I have the following for instance:
x = [{'A':1},{'A':1},{'A':2},{'B':1},{'B':1},{'B':2},{'B':3},{'C':1},{'D':1}]
and I would like to get a dictionary like this:
x = [{'A': [1,2], 'B': [1,2,3], 'C':[1], 'D': [1]}]
Do you have any idea how I could get this please?
You could use a collections.defaultdict of sets to collect unique values, then convert the final result to a dictionary with values as lists using a dict comprehension:
from collections import defaultdict
lst = [{'A':1},{'A':1},{'A':2},{'B':1},{'B':1},{'B':2},{'B':3},{'C':1},{'D':1}]
result = defaultdict(set)
for dic in lst:
for key, value in dic.items():
result[key].add(value)
print({key: list(value) for key, value in result.items()})
Output:
{'A': [1, 2], 'B': [1, 2, 3], 'C': [1], 'D': [1]}
Although its probably better to add your data directly to the defaultdict to begin with, instead of creating a list of singleton dictionaries(don't recommend this data structure) then converting the result.
Using dict.setdefault
Ex:
x = [{'A':1},{'A':1},{'A':2},{'B':1},{'B':1},{'B':2},{'B':3},{'C':1},{'D':1}]
res = {}
for i in x:
for k, v in i.items():
res.setdefault(k, set()).add(v)
#or res = [{k: list(v) for k, v in res.items()}]
print(res)
Output:
{'A': {1, 2}, 'B': {1, 2, 3}, 'C': {1}, 'D': {1}}

Python Object Referencing Workaround

Consider the code below:
outer_list = ['a', 'b', 0]
inner_list = [1, 2, 3]
final = []
for item in inner_list:
outer_list[-1] = item
final.append(outer_list)
print(final)
with output : [['a', 'b', 3], ['a', 'b', 3], ['a', 'b', 3]]
My intended output is: [['a', 'b', 1], ['a', 'b', 2], ['a', 'b', 3]]
I understand this has to do with the fact that Python uses object referencing but i cant seem to find a way around this.
Anyone with a solution or alternative i'd appreciate
Yes you're right. Your assignment at line 6 modifies list outer_list and your append call add references to that list in final. You can work on a copy of outer_list to get your result :
outer_list = ['a', 'b', 0]
inner_list = [1, 2, 3]
final = []
for item in inner_list:
l = outer_list.copy()
l[-1] = item
final.append(l)
print(final)
List concatenation returns a new list object.
outer_list = ['a', 'b']
inner_list = [1, 2, 3]
print([outer_list + [item] for item in inner_list])
You could also achieve using map
print(list(map(lambda item: outer_list + [item], inner_list )))

nested dictionary based on 3 dataframe columns

im trying to build a nested dictionary based on 3 pandas df columns:
dataframe: stops
columns: 'direction' (1-2) ,'stop_num'(1-23 if the direction is 1 and 100-2300 if direction is 2),'name_eng'
what i was trying to do is:
dct = {x: {y:z} for x, y, z in zip(stops['direction'],stops['name_eng'],stops['stop_num'])}
the result i get is a nested dictionary indeed but for unknown reason i get only the last value in y:z so the dictionary look like:
{1:{1:'aaa'},'2:{100:'bbb'}}
any idea what am i doing wrong?
what i need is a nested dictionary with two dictionaries for each direction.
thanks!
Imagine your columns are:
1 a 1a
1 b 1b
2 a 2a
2 b 2b
Now, try your code:
>>> {x: {y:z} for x, y, z in zip([1,1,2,2], ['a', 'b', 'a', 'b'], ['1a', '1b', '2a', '2b'])}
{1: {'b': '1b'}, 2: {'b': '2b'}}
You have a loop over the tuples: (1, 'a', '1a'), (1, 'b', '1b'), (2, 'a', '2a'), (2, 'b', '2b').
The first element of the tuple is the "main" key of your dictionary. Thus, the dict is {1: {'a':'1a'}} after the first tuple.
Then comes (1, 'b', '1b'). The value of the main key 1 is overwritten and the dict becomes: {1: {'b':'1b'}}.
The next steps are: {1: {'b':'1b'}, 2: {'a': '2a'}} and {1: {'b': '1b'}, 2: {'b': '2b'}}
To avoid the overwrite, you can do:
>>> d = {}
>>> for x, y, z in zip([1,1,2,2], ['a', 'b', 'a', 'b'], ['1a', '1b', '2a', '2b']):
... d.setdefault(x, {}).update({y:z})
...
>>> d
{1: {'a': '1a', 'b': '1b'}, 2: {'a': '2a', 'b': '2b'}}
The idea is to create a new dict for every new main key (setdefault(..., {})) and to update the dict associated with the main key (update({y:z})).
If you want a dict comprehension, this one will work:
>>> {x: {y:z for k, y, z in zip([1,1,2,2], ['a', 'b', 'a', 'b'], ['1a', '1b', '2a', '2b']) if k==x} for x in set([1,1,2,2])}
{1: {'a': '1a', 'b': '1b'}, 2: {'a': '2a', 'b': '2b'}}
But it's far less efficient than the for loop because you loop once over the first column to get the main keys, then once again over all the rows, for every main key.

add current position+1 or -1 dict value to dict python

for example:
s = 'abc'
number = 1
I want to write a function that return a dict like {'a': {'a', 'b'}, 'b': {'a', 'b', 'c'}, 'c': {'b', 'c'}}
number determine how many adjacent letters next to the current key.
def test(s : str, num : int) -> {str:{str}}:
dict = {}
for word in s:
dict[word] = word
return dict
i can only write one return the same key and value. any suggestions?
Try something like:
>>> s='abc'
>>> n=1
>>> {c:{e for e in[s[i-n:i],c,s[i+1:i+1+n]] if e} for i, c in enumerate(s)}
{'a': {'a', 'b'}, 'b': {'a', 'b', 'c'}, 'c': {'b', 'c'}}

How do i print a positional argument

I have this example code and nee to print out e.g. the second argument position alone, how do i do it?
def fun(a, b, c):
d = locals()
e = d
print (e)
print (locals())
fun(None, 2, None)
print(b) ... or I do not understand the question.
Update: If you mean to know the names of the arguments, you may want to use the standard module named inspect. Try the following:
#!python3
import inspect
def fun(a, b, c):
d = locals()
e = d
print (e)
print (locals())
# Here for observing from inside.
argspec = inspect.getfullargspec(fun)
print(argspec.args)
for arg in argspec.args:
print('argument', repr(arg), '=', repr(d[arg]))
# You can use indexing of the arg names if you like. Then the name
# is used for looking in locals() -- here you have it in d.
args = argspec.args
print(d[args[0]])
print(d[args[1]])
print(d[args[2]])
fun(None, 2, None)
# Here for observing from outside.
argspec = inspect.getfullargspec(fun)
print(argspec.args)
for n, arg in enumerate(argspec.args, 1):
print('argument', n, 'is named', repr(arg))
You should observe:
{'a': None, 'b': 2, 'c': None}
{'d': {...}, 'e': {...}, 'a': None, 'b': 2, 'c': None}
['a', 'b', 'c']
argument 'a' = None
argument 'b' = 2
argument 'c' = None
None
2
None
['a', 'b', 'c']
argument 1 is named 'a'
argument 2 is named 'b'
argument 3 is named 'c'
See the doc http://docs.python.org/3.3/library/inspect.html#inspect.getfullargspec

Resources