How can I find matches in two arrays with dictionaries in them? - python

I have the following problem. I want to compare two lists with dictionaries and find matches.
Schema List A:
[
{
authorID: string,
articleID: string
}
]
Schema List B:
[
{
authorID: string,
firstName: string,
lastName: string
}
]
The length of the list A is 1653 and the list B is 1344 long.
This is my code with which I tried to compare the lists and resave the matches.
def unitedListArticleAuthor(listA, listB):
new_list = []
for article in listA:
for author in listB:
if article["authorID"] == author["authorID"]:
new_list.append({
"articleID": article["articleID"],
"authorID": article["authorID"],
"firstName": author["firstName"],
"lastName": author["lastName"],
})
Now the length of the new list is 1667. That means somewhere a mistake must have happened, because the list A is the biggest and only has to be matched with the list B. Only the author ID's should be matched to add the name of the author to each article.
What is my mistake?

As Sayse said, it is likely that there are multiple instances of the same authorID.
You seem really sure that this is not the case, but try adding a break statement like this:
def unitedListArticleAuthor(listA, listB):
new_list = []
for article in listA:
for author in listB:
if article["authorID"] == author["authorID"]:
new_list.append({
"articleID": article["articleID"],
"authorID": article["authorID"],
"firstName": author["firstName"],
"lastName": author["lastName"],
})
break

Related

How can I write a function that doesn't print out uppercase words from the list?

I've a list ["ABC", "JAVA", "Python", "user", "CODE"] and I want the functions print out ["Python", "user"].
Here is my progress so far:
def no_upper(my_list : list):
new_list = my_list
for word in my_list:
if word.isupper():
my_list.remove(word)
return new_list
if __name__ == "__main__":
my_list = ["ABC", "JAVA", "Python", "user", "CODE"]
new_list = no_upper(my_list)
print(new_list)
If you objective is to "remove" words written entirely in capital letters:
lst = ["ABC", "JAVA", "Python", "user", "CODE"]
[x for x in lst if x != x.upper()]
OUTPUT
['Python', 'user']
Enclose your strings in quotes.
Your function tries to modify my_list rather than actually building your new_list. Just build the new list and return it without trying to remove the items from the input -- modifying a list you're iterating over will usually have unexpected/buggy results, and even if it worked to do that, there's no good reason to mess around with the caller's data that way.
>>> def no_upper(my_list: list) -> list:
... return [word for word in my_list if not word.isupper()]
...
>>> no_upper(["ABC", "JAVA", "Python", "user", "CODE"])
['Python', 'user']
Try with a list like this:
my_list = ["ABC", "Java", "Python"]
Unless Python will raise an UnboundLocalError, since ABC without quotes ('') or double quotes ("") is parsed as a variable, but there's no ABC variable in your program.
You can also use inline syntax to solve your problem, like this:
def noUpper(l: list):
return [x for x in list if not x.isupper()]

Find *all* shortest strings in a list

I want to print the shortest strings in a list.
for example the output for:
names=["Hans", "Anna", "Vladimir", "Michael", "Ed", "Susan", "Janice", "Jo"]
will be
"Ed", "Jo"
The output for
names=["Hans", "Anna", "Vladimir", "Michael", "Ed", "Susan", "Janice", "Jo", "Al"]
will be:
"Ed", "Jo", "Al"
[x for x in names if x==min(names, key=len)]
will get only one of them, but will not help in cases where there is more than one.
Pass len as key to the min method and get the length of the smallest string and then use list comprehension as:
names=["Hans", "Anna", "Vladimir", "Michael", "Ed", "Susan", "Janice", "Jo"]
smallest_len = len(min(names, key=len))
smallest = [name for name in names if len(name) == smallest_len]
First find the length of the shortest string:
shortest = min(map(len, names))
Now just find all strings with that length:
print([name for name in names if len(name) == shortest])
names = ["Hans", "Anna", "Vladimir", "Michael", "Ed", "Susan", "Janice", "Jo"]
smallest_len = len(min(names, key=len))
shorts = []
for name in names:
if len(name) == smallest_len:
shorts.append(name)
print(shorts)
Based on the question, my guess is that you're a Python beginner, so I really don't want to introduce you to advanced pythonic concepts.
Using no advanced concepts of the language, it's straight forward:
names = ["Hans", "Anna", "Vladimir", "Michael", "Ed", "Susan", "Janice", "Jo"]
def get_all_shortest_strings(strings: list) -> list: # Accept a list of strings and return a list of strings as well
shortest_strings = [] # Start with an empty list. This is the identity element for list addition
shortest_length = 9999999999999999 # Start with a huge number. This is the identity element for the minimum() function
for string in strings: # Go through all strings.
if len(string) == shortest_length: # Found one of the same length?
shortest_strings.append(string) # Add it!
if len(string) < shortest_length: # Found a shorter one?
shortest_strings = [] # Clear all longer ones
shortest_strings.append(string) # Add the one we just found
shortest_length = len(string) # remember the length
return shortest_strings
assert ["Ed", "Jo"] == get_all_shortest_strings(names)
You may want to read about the identity element. Basically it says that this element will not change the result of a given operation.
You may also not have seen assert before. You can use asserts for checking if your code works, without the need of printing something.
To loop only once, and call len on each item once:
names = [
"Hans", "Anna", "Vladimir", "Michael", "Ed", "Susan", "Janice", "Jo", "Al"
]
def find_shortests(names: list):
result = []
shortest = float("inf")
for x in names:
n = len(x)
if n > shortest:
continue
if n < shortest:
result.clear()
shortest = n
result.append(x)
return result
print(find_shortests(names))
# ['Ed', 'Jo', 'Al']

if else nested for loops using python list comprehension

Can anyone help me figure the list comprehension way of producing following output -
Let given list be -
results = [
{"id": 1, "name": "input"},
{"name": "status", "desc": "Status"},
{"name": "entity", "fields": [
{"id": 101, "name": "value"},
{"id": 102, "name": "address"}]
}
]
And I am looking for output in the form of list.
The code to get the output is:
output = []
for eachDict in results:
if "fields" in eachDict:
for field in eachDict["fields"]:
output.append(eachDict["name"]+"."+field["name"])
else:
output.append(eachDict["name"])
Thus the output using above code is -
['input', 'status', 'entity.value', 'entity.address']
Is it possible to get similar output using if else nested for loops in list Comprehension?
I am having trouble trying to get access to that inner for loop in if condition of list Comprehension
My attempt -
output = [eachDict["name"]+"."+field["name"] for field in eachDict["fields"] if "fields" in eachDict else eachDict["name"] for eachDict in results]
One way to transform your code into workable code would be to make the inner loop produce lists, and then flatten the result afterward.
sum(([d['name'] + '.' + f['name'] for f in d['fields']]
if d.get('fields') else [d['name']] for d in results), [])
A list comprehension has a fixed number of (nested) loops. So must have two loops here, one over the top-level dictionaries, and one over the fields. The trick is to produce one iteration in the nested loop if there are no fields:
[d['name'] + fieldname
for d in results
for fieldname in (
('.' + sub['name'] for sub in d['fields']) if 'fields' in d else
('',))
]
The for fieldname loop loops either over the names of the fields sub-directories (with '.' prefixed), or over a tuple with just a single empty string in it.
Note that this isn't actually all that readable. I'd delegate producing the fieldname loop to a helper generator function to improve that:
def fieldnames(d):
if 'fields' in d:
for sub in d['fields']:
yield '.' + sub['name']
else:
yield ''
[d['name'] + fieldname for d in results for fieldname in fieldnames(d)]
Demo:
>>> def fieldnames(d):
... if 'fields' in d:
... for sub in d['fields']:
... yield '.' + sub['name']
... else:
... yield ''
...
>>> [d['name'] + fieldname for d in results for fieldname in fieldnames(d)]
['input', 'status', 'entity.value', 'entity.address']

Python remove elements matching pattern from array

I have a dictionary that contains strings as keys and lists as values.
I'd like to remove all list elements that contain the strings "food", "staging", "msatl" and "azeus". I have the below code already, but am having a hard time applying the logic I have in filterIP to the rest of the strings I have.
def filterIP(fullList):
regexIP = re.compile(r'\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$')
return filter(lambda i: not regexIP.search(i), fullList)
groups = {key : [domain.replace('fake.com', 'env.fake.com')
for domain in filterIP(list(set(items)))]
for (key, items) in groups.iteritems() }
for key, value in groups.iteritems():
value.sort()
meta = { "_meta" : { "hostvars" : hostvars } }
groups.update(meta)
print(self.json_format_dict(groups, pretty=True))
Example of current output
"role_thumper": [
"thumper-msatl1-prod-1.env.fake.com",
"thumper-msatl1-prod-2.env.fake.com",
"thumper-rwva1-prod-1.env.fake.com",
"thumper-rwva1-prod-2.env.fake.com",
"thumper-rwva1-prod-3.env.fake.com",
"thumper-rwva1-prod-4.env.fake.com",
"thumper-rwva1-prod-5.env.fake.com",
"thumper-rwva1-prod-6.env.fake.com",
"thumper-staging-1.env.fake.com"
],
"role_thumper_mongo": [
"thumper-mongo-staging-1.env.fake.com",
"thumper-mongo-staging-2.env.fake.com",
"thumpermongo-rwva1-staging-1.env.fake.com",
"thumpermongo-rwva1-staging-2.env.fake.com"
],
"role_thumper_mongo_arb": [
"thumper-mongo-arb-staging-1.env.fake.com",
"thumpermongo-arb-rwva1-staging-1.env.fake.com"
],
A list comprehension is what you're after
x= ["a", "b", "aa", "aba"]
x_filtered = [i for i in x if "a" not in i]
print(x_filtered)
>>> ['b']
This is just shorthand for a for loop.
x_filtered = []
for i in x:
if "a" not in i:
x_filtered.append(i)
A simple way to accomplish your task would be to iterate over each lists in the dictionary. Create new lists based upon your criteria, and assign the new lists to the same keys but in a new dictionary. Here is how that would look like in code:
def filter_words(groups, words):
d = {}
for key, domains in groups.iteritems():
new_domains = []
for domain in domains:
if not any(word in domain for word in words):
new_domains.append(domain)
d[key] = new_domains
return d
And you would call it like so:
groups = filter_words(groups, {"food", "staging", "msatl" and "azeus"})
The "meat" of the code above is the second for loop:
for domain in domains:
if not any(word in domain for word in words):
new_domains.append(domain)
This code goes over each string in the current key's list, and filters out all invalid strings according to a list of invalid words.
If I understand you correctly, this might help.
Set up an exclude list:
exclude= ["food", "staging", "msatl", "azeus"]
Test list ( I couldn't really find instances in your examples)
test= ["food", "staging", "msatl", "azeus", "a", "bstaging"]
Run list comprehension (the name of iterators don't matter, you can pick more appropriate ones)
result= [i for i in test if not any([e for e in exclude if e in i])]
result
['a']
The answer above by #Julian gives a good explanation of what list comprehensions do. This uses two of them, the any part is True if there is any match in the exclude list.
Hope this helps.

How to tell if a list inside a dict is empty

I have a movietimes={}
It a dict I make by this code:
for i in Showtime.objects.filter(movie_id=movieid,theater_id=theaterid,datetime__range=(today,tomorrow))):
if i.mvtype not in movietimes:
movietimes[i.mvtype] = []
if not i.movietime > today :
movietimes[i.mvtype].append(i.movietime.strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
if not movietimes : #this have bug
for i in Showtime.objects.filter(movie_id=movieid,datetime__range=(yesterday,today)):
if i.mvtype not in movietimes:
movietimes[i.mvtype] = []
movietimes[i.mvtype].append(i.movietime.strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
return movietimes
result like this:
"Times": {
"ONE: [
"2014-12-24T10:40:00.000000Z",
"2014-12-24T12:45:00.000000Z",
"2014-12-25T14:50:00.000000Z"
]
}
I want to ask how can't I judge that if the [] in the 'ONE' part is null ??
I can't use if not movietimes={}: ,because there is u'ONE': [] in the dict
I have to judge the first list in the dict is empty . And there are many types u'ONE',u'TWO',u'Three'
they are catch by i.mvtype
{u'ONE': []}
{u'TWO': []}
{u'Three': []}
Please help me ,Thank you
if not movietimes["Times"]["ONE"]:
# you have empty list
That is presuming by first you mean the key ONE as dicts are not ordered
If you want to see if there is any empty list and your dict is like below:
movietimes = {"Times":{"ONE":[2],"TWO":[]}}
for val in movietimes["Times"].itervalues():
if not any(x for x in val):
# you have empty list

Categories

Resources