I'm using a dictionary with the id as the key and the names as the values. What I'm trying to do is get the names in the values that have the same name in them and put them in a list. Like for example with the name tim:
{'id 1': ['timmeh', 'user543', 'tim'], 'id 2': ['tim', 'timmeh', '!anon0543']}
whois_list = ['timmeh', 'user543', 'tim', '!anon0543']
The bot would append the names that are not in list yet. This is the code to execute this example:
def who(name):
whois_list = []
if not any(l for l in whois.whoisDB.values() if name.lower() in l):
return 'No alias found for <b>%s</b>." % name.title()
else:
for l in whois.whoisDB.values():
if name.lower() in l:
for names in l:
if names not in whois_list
whois_list.append(names)
return "Possible alias found for <b>%s</b>: %s" % (name.title(), whois_list)
The issue is: I do not want to have a double loop in this code, but I'm not really sure how to do it, if it's possible.
A logically equivalent, but shorter and more efficient solution is to use sets instead of lists.
Your innermost for loop simply extends whois_list with every non-duplicate name in l. If you originally define whois_list = set([]) then you can replace the three lines of the inner for loop with:
whois_list = whois_list.union(l)
For example,
>>> a = set([1,2,3])
>>> a = a.union([3,4,5])
>>> a
set([1, 2, 3, 4, 5])
You'll notice a prints out slightly differently, indicating that it is a set instead of a list. If this is a problem, you could convert it right before your return statement as in
>>> a = list(a)
>>> a
[1, 2, 3, 4, 5]
Related
I have a data structure that looks something like this:
someData = {"apple":{"taste":"not bad","colors":["red","yellow"]},
"banana":{"taste":"perfection","shape":"banana shaped"},
"some list":[6,5,3,2,4,6,7]}
and a list of keys which describes a path to some item in this structure
someList = ["apple","colors",2]
I already have a function getPath(path) (see below) that is supposed to return a pointer to the selected object. It works fine for reading, but I get into trouble when trying to write
print(getPath(someList))
>> yellow
getPath(someList) = "green"
>> SyntaxError: can't assign to function call
a = getPath(someList)
a = "green"
print(getPath(someList))
>> "yellow"
Is there a way to make this work? Maybe like this:
someFunc(someList, "green")
print(getPath(someList))
>> green
This question looks like this question, except that I want to write something to that item, and not just read it.
My actual data can be seen here (I used json.loads() to parse the data). Note that I plan on adding stuff to this structure. I want a general approach to future proof the project.
My code:
def getPath(path):
nowSelection = jsonData
for i in path:
nowSelection = nowSelection[i]
return nowSelection
The result you're getting from getPath() is the immutable value from a dict or list. This value does not even know it's stored in a dict or list, and there's nothing you can do to change it. You have to change the dict/list itself.
Example:
a = {'hello': [0, 1, 2], 'world': 2}
b = a['hello'][1]
b = 99 # a is completely unaffected by this
Compare with:
a = {'hello': [0, 1, 2], 'world': 2}
b = a['hello'] # b is a list, which you can change
b[1] = 99 # now a is {'hello': [0, 99, 2], 'world': 2}
In your case, instead of following the path all the way to the value you want, go all the way except the last step, and then modify the dict/list you get from the penultimate step:
getPath(["apple","colors",2]) = "green" # doesn't work
getPath(["apple","colors"])[2] = "green" # should work
You could cache your getPath using custom caching function that allows you to manually populate saved cache.
from functools import wraps
def cached(func):
func.cache = {}
#wraps(func)
def wrapper(*args):
try:
return func.cache[args]
except KeyError:
func.cache[args] = result = func(*args)
return result
return wrapper
#cached
def getPath(l):
...
getPath.cache[(someList, )] = 'green'
getPath(someList) # -> 'green'
You can't literally do what you're trying to do. I think the closest you could get is to pass the new value in, then manually reassign it within the function:
someData = {"apple":{"taste":"not bad","colors":["red","yellow"]}, "banana":{"taste":"perfection","shape":"banana shaped"}, "some list":[6,5,3,2,4,6,7]}
def setPath(path, newElement):
nowSelection = someData
for i in path[:-1]: # Remove the last element of the path
nowSelection = nowSelection[i]
nowSelection[path[-1]] = newElement # Then use the last element here to do a reassignment
someList = ["apple","colors",1]
setPath(someList, "green")
print(someData)
{'apple': {'taste': 'not bad', 'colors': ['red', 'green']}, 'banana': {'taste': 'perfection', 'shape': 'banana shaped'}, 'some list': [6, 5, 3, 2, 4, 6, 7]}
I renamed it to setPath to reflect its purpose better.
Assume I have a python dictionary with 2 keys.
dic = {0:'Hi!', 1:'Hello!'}
What I want to do is to extend this dictionary by duplicating itself, but change the key value.
For example, if I have a code
dic = {0:'Hi!', 1:'Hello'}
multiplier = 3
def DictionaryExtend(number_of_multiplier, dictionary):
"Function code"
then the result should look like
>>> DictionaryExtend(multiplier, dic)
>>> dic
>>> dic = {0:'Hi!', 1:'Hello', 2:'Hi!', 3:'Hello', 4:'Hi!', 5:'Hello'}
In this case, I changed the key values by adding the multipler at each duplication step. What's the efficient way of doing this?
Plus, I'm also planning to do the same job for list variable. I mean, extend a list by duplicating itself and change some values like above exmple. Any suggestion for this would be helpful, too!
You can try itertools to repeat the values and OrderedDict to maintain input order.
import itertools as it
import collections as ct
def extend_dict(multiplier, dict_):
"""Return a dictionary of repeated values."""
return dict(enumerate(it.chain(*it.repeat(dict_.values(), multiplier))))
d = ct.OrderedDict({0:'Hi!', 1:'Hello!'})
multiplier = 3
extend_dict(multiplier, d)
# {0: 'Hi!', 1: 'Hello!', 2: 'Hi!', 3: 'Hello!', 4: 'Hi!', 5: 'Hello!'}
Regarding handling other collection types, it is not clear what output is desired, but the following modification reproduces the latter and works for lists as well:
def extend_collection(multiplier, iterable):
"""Return a collection of repeated values."""
repeat_values = lambda x: it.chain(*it.repeat(x, multiplier))
try:
iterable = iterable.values()
except AttributeError:
result = list(repeat_values(iterable))
else:
result = dict(enumerate(repeat_values(iterable)))
return result
lst = ['Hi!', 'Hello!']
multiplier = 3
extend_collection(multiplier, lst)
# ['Hi!', 'Hello!', 'Hi!', 'Hello!', 'Hi!', 'Hello!']
It's not immediately clear why you might want to do this. If the keys are always consecutive integers then you probably just want a list.
Anyway, here's a snippet:
def dictExtender(multiplier, d):
return dict(zip(range(multiplier * len(d)), list(d.values()) * multiplier))
I don't think you need to use inheritance to achieve that. It's also unclear what the keys should be in the resulting dictionary.
If the keys are always consecutive integers, then why not use a list?
origin = ['Hi', 'Hello']
extended = origin * 3
extended
>> ['Hi', 'Hello', 'Hi', 'Hello', 'Hi', 'Hello']
extended[4]
>> 'Hi'
If you want to perform a different operation with the keys, then simply:
mult_key = lambda key: [key,key+2,key+4] # just an example, this can be any custom implementation but beware of duplicate keys
dic = {0:'Hi', 1:'Hello'}
extended = { mkey:dic[key] for key in dic for mkey in mult_key(key) }
extended
>> {0:'Hi', 1:'Hello', 2:'Hi', 3:'Hello', 4:'Hi', 5:'Hello'}
You don't need to extend anything, you need to pick a better input format or a more appropriate type.
As others have mentioned, you need a list, not an extended dict or OrderedDict. Here's an example with lines.txt:
1:Hello!
0: Hi.
2: pylang
And here's a way to parse the lines in the correct order:
def extract_number_and_text(line):
number, text = line.split(':')
return (int(number), text.strip())
with open('lines.txt') as f:
lines = f.readlines()
data = [extract_number_and_text(line) for line in lines]
print(data)
# [(1, 'Hello!'), (0, 'Hi.'), (2, 'pylang')]
sorted_text = [text for i,text in sorted(data)]
print(sorted_text)
# ['Hi.', 'Hello!', 'pylang']
print(sorted_text * 2)
# ['Hi.', 'Hello!', 'pylang', 'Hi.', 'Hello!', 'pylang']
print(list(enumerate(sorted_text * 2)))
# [(0, 'Hi.'), (1, 'Hello!'), (2, 'pylang'), (3, 'Hi.'), (4, 'Hello!'), (5, 'pylang')]
I'm trying to get the matching IDs and store the data into one list. I have a list of dictionaries:
list = [
{'id':'123','name':'Jason','location': 'McHale'},
{'id':'432','name':'Tom','location': 'Sydney'},
{'id':'123','name':'Jason','location':'Tompson Hall'}
]
Expected output would be something like
# {'id':'123','name':'Jason','location': ['McHale', 'Tompson Hall']},
# {'id':'432','name':'Tom','location': 'Sydney'},
How can I get matching data based on dict ID value? I've tried:
for item in mylist:
list2 = []
row = any(list['id'] == list.id for id in list)
list2.append(row)
This doesn't work (it throws: TypeError: tuple indices must be integers or slices, not str). How can I get all items with the same ID and store into one dict?
First, you're iterating through the list of dictionaries in your for loop, but never referencing the dictionaries, which you're storing in item. I think when you wrote list[id] you mean item[id].
Second, any() returns a boolean (true or false), which isn't what you want. Instead, maybe try row = [dic for dic in list if dic['id'] == item['id']]
Third, if you define list2 within your for loop, it will go away every iteration. Move list2 = [] before the for loop.
That should give you a good start. Remember that row is just a list of all dictionaries that have the same id.
I would use kdopen's approach along with a merging method after converting the dictionary entries I expect to become lists into lists. Of course if you want to avoid redundancy then make them sets.
mylist = [
{'id':'123','name':['Jason'],'location': ['McHale']},
{'id':'432','name':['Tom'],'location': ['Sydney']},
{'id':'123','name':['Jason'],'location':['Tompson Hall']}
]
def merge(mylist,ID):
matches = [d for d in mylist if d['id']== ID]
shell = {'id':ID,'name':[],'location':[]}
for m in matches:
shell['name']+=m['name']
shell['location']+=m['location']
mylist.remove(m)
mylist.append(shell)
return mylist
updated_list = merge(mylist,'123')
Given this input
mylist = [
{'id':'123','name':'Jason','location': 'McHale'},
{'id':'432','name':'Tom','location': 'Sydney'},
{'id':'123','name':'Jason','location':'Tompson Hall'}
]
You can just extract it with a comprehension
matched = [d for d in mylist if d['id'] == '123']
Then you want to merge the locations. Assuming matched is not empty
final = matched[0]
final['location'] = [d['location'] for d in matched]
Here it is in the interpreter
In [1]: mylist = [
...: {'id':'123','name':'Jason','location': 'McHale'},
...: {'id':'432','name':'Tom','location': 'Sydney'},
...: {'id':'123','name':'Jason','location':'Tompson Hall'}
...: ]
In [2]: matched = [d for d in mylist if d['id'] == '123']
In [3]: final=matched[0]
In [4]: final['location'] = [d['location'] for d in matched]
In [5]: final
Out[5]: {'id': '123', 'location': ['McHale', 'Tompson Hall'], 'name': 'Jason'}
Obviously, you'd want to replace '123' with a variable holding the desired id value.
Wrapping it all up in a function:
def merge_all(df):
ids = {d['id'] for d in df}
result = []
for id in ids:
matches = [d for d in df if d['id'] == id]
combined = matches[0]
combined['location'] = [d['location'] for d in matches]
result.append(combined)
return result
Also, please don't use list as a variable name. It shadows the builtin list class.
I'd like to know how to parse (or split) and element of a list?
I have a list of lists (of string) such as:
resultList = [['TWP-883 PASS'], ['TWP-1080 PASS'], ['TWP-1081 PASS']]
where:
resultList[0] = ['TWP-883 PASS']
resultList[1] = ['TWP-1080 PASS']
essentially, I need a variable for the two entries in each element of the list. For example:
issueId = 'TWP-883'
status = 'PASS'
What would allow for iterating through this list and parsing such as above?
Well that's as simple as:
# You can also assign as you iterate as suggested in the comments.
for issue, status in resultList:
print issue, status
This outputs
TWP-883 PASS
TWP-1080 PASS
TWP-1081 PASS
TWP-1082 PASS
TWP-884 FAIL
TWP-885 PASS
Here's another example:
>>> x = [1, 2] # or (1, 2), or '12' works with collections
>>> y, z = x
>>> y
1
>>> z
2
>>>
Incidentally, in Python 3.x, you can also do:
In [1]: x = [1, 2, 3, 4]
In [2]: y, z, *rest = x
In [3]: y
Out[3]: 1
In [4]: z
Out[4]: 2
In [5]: rest
Out[5]: [3, 4]
You just need a simple for loop that exploits Python's tuple unpacking machinery.
for issueId, status in resultList:
# Do stuff with issueId and status
Note: I changed this answer to reflect an edit of the question. Specifically, I added a split() to separate the strings in the nested lists into two strings (issueId and status).
I would use list and dictionary comprehensions to turn your list of lists into a list of dictionaries with the keys issueId and status:
resultList = [['TWP-883 PASS'], ['TWP-1080 PASS'], ['TWP-1081 PASS']]
result_dicts = [{("issueId","status")[x[0]]:x[1] for x in enumerate(lst[0].split())} for lst in resultList]
Lookups can now be done in this way:
>>> result_dicts[0]["status"]
'PASS'
>>> result_dicts[0]["issueId"]
'TWP-883'
>>> result_dicts[1]
{'status': 'PASS', 'issueId': 'TWP-1080'}
>>>
To declare variables for each value in each dictionary in the list and print them, use the code below:
for entry in result_dicts:
issueId = entry["issueId"]
status = entry["status"]
print("The status of {0: <10} is {1}".format(issueId, status))
Output:
The status of TWP-883 is PASS
The status of TWP-1080 is PASS
The status of TWP-1081 is PASS
If you wish to do more transformations later, use a genexp,
g = (func(issueid, status) for issueid, status in resultList) # func returns non-None objects
If you just want to consume the iterable,
for issueid, status in resultList:
# print(issueid, status)
# whatever
You can get the list of the strings by
issueId = [y for x in resultList for (i,y) in enumerate(x.split()) if(i%2==0)]
status = [y for x in resultList for (i,y) in enumerate(x.split()) if(i%2==1)]
to go through every issueID and corrosponding status you can use
for id,st in zip(issueId,status):
print(id, " : ", st)
Is it possible in python to add a tuple as a value in a dictionary?
And if it is,how can we add a new value, then? And how can we remove and change it?
>>> a = {'tuple': (23, 32)}
>>> a
{'tuple': (23, 32)}
>>> a['tuple'] = (42, 24)
>>> a
{'tuple': (42, 24)}
>>> del a['tuple']
>>> a
{}
if you meant to use tuples as keys you could do:
>>> b = {(23, 32): 'tuple as key'}
>>> b
{(23, 32): 'tuple as key'}
>>> b[23, 32] = 42
>>> b
{(23, 32): 42}
Generally speaking there is nothing specific about tuples being in dictionary, they keep behaving as tuples.
Since tuples are immutable, you cannot add a value to the tuple. What you can do, is construct a new tuple from the current tuple and an extra value. The += operator does this for you, provided the left argument is a variable (or in this case a dictionary value):
>>> t = {'k': (1, 2)}
>>> t['k'] += (3,)
>>> t
{'k': (1, 2, 3)}
Regardless, if you plan on altering the tuple value, perhaps it's better to store lists? Those are mutable.
Edit: Since you updated your question†, observe the following:
>>> d = {42: ('name', 'date')}
>>> d[42][0] = 'name2'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
This happens because, as stated before, tuples are immutable. You cannot change them. If you want to change them, then in fact you'll have to create a new one. Thus:
>>> d[42] = ('name2', d[42][2])
>>> d
{42: ('name2', 'date')}
As a side note, you may want to use namedtuples. They work just like regular tuples, but allow you to refer to elements within the tuple by name:
>>> from collections import namedtuple
>>> Person = namedtuple('Person', 'name date')
>>> t = {42: Person('name', 'date')}
>>> t[42] = Person('name2', t[42].date)
>>> t
{42: Person(name='name2', date='date')}
†: Next time please edit your actual question. Do not post an answer containing only further questions. This is not a forum.
You can't change a tuple itself. You have to replace it by a different tuple.
When you use a list, you could also add values to it (changing the list itself) without need to replace it:
>> a = {'list': (23, 32)}
>> a
{'list': [23, 32]}
>> a['list'].append(99)
>> a
{'list': [23, 32, 99]}
In most cases, lists can be used as replacement for tuples (since as much I know they support all tuple functions -- this is duck typing, man!)
t1=('name','date')
t2=('x','y')
# "Number" is a String key!
d1={"Number":t1}
# Update the value of "Number"
d1["Number"] = t2
# Use a tuple as key, and another tuple as value
d1[t1] = t2
# Obtain values (getters)
# Can throw a KeyError if "Number" not a key
foo = d1["Number"]
# Does not throw a key error, t1 is the value if "Number" is not in the dict
d1.get("Number", t1)
# t3 now is the same as t1
t3 = d1[ ('name', 'date') ]
You updated your question again. Please take a look at Python dict docs. Python documentation is one of it's strong points! And play with the interpreter (python)on the command line! But let's continue.
initially key 0
d[0] = ('name', datetime.now())
id known
d1 = d[0]
del d[0]
name changed
tmp = d1
d1 = ( newname, tmp1 )
And please consider using a
class Person(object):
personIdCounter = 1
def __init__(self):
self.id = Person.personIdCounter
Person.personIdCounter += 1
self.name
self.date
then
persons = {}
person = Person()
persons[person.id] = person
person.name = "something"
persons[1].name = "something else"
That looks better than a tuple and models your data better.