Nested List -Python - python

I have two list . i want to compare with each other with the list index[1][2][3] of "a" of each list with other list index[1][2][3] of "b" .If its a match then ignore , if not then return the whole list.
a = [['Eth1/1/13', 'Marketing', 'connected', '10', 'full', 'a-1000'], ['Eth1/1/14', 'NETFLOW02', 'connected', '10', 'full', '100']]
b = [['Eth1/1/13', 'NETFLOW02', 'connected', '15', 'full', '100'], ['Eth1/1/14', 'Marketing', 'connected', '10', 'full', 'a-1000']]
Desired Output :
Diff a:
Eth1/1/14 NETFLOW02 connected 10 full 100
Diff b:
Eth1/1/13 NETFLOW02 connected 15 full 100
What i am trying :
p = [i for i in a if i not in b]
for item in p:
print item[0]
print "\n++++++++++++++++++++++++++++++\n"
q = [i for i in b if i not in a]
for item in q:
print item[0]
tried below but only managed to match index 1 of inner list , index 2 and 3 still need to be matched..
[o for o in a if o[1] not in [n[1] for n in b]
I am not getting the expected output.Any idea how to do this ?

for sublista in a:
if not any(sublista[1:4] == sublistb[1:4] for sublistb in b):
print(sublista)
You need an inner loop so that each sub-list from list a can be compared to each sub-list in list b. The inner loop is accomplished with a generator expression. Slices are used to to compare only a portion of the sub-lists. The built-in function any consumes the generator expression; it is lazy and will return True with the first True equivalency comparison. This will print each sub-list in a that does not have a match in b - to print each sub-list in b that does not have a match in a, put b in the outer loop and a in the inner loop.
Here is an equivalent Without using a generator expression or any:
for sublista in a:
equal = False
for sublistb in b:
if sublista[1:4] == sublistb[1:4]:
break
else:
print(sublista)
Sometimes it is nice to use operator.itemgetter so you can use names for the slices which can make the code more intelligible.:
import operator
good_stuff = operator.itemgetter(1,2,3)
for sublista in a:
if not any(good_stuff(sublista) == good_stuff(sublistb) for sublistb in b):
print(sublista)
itertools.product conveniently generates pairs and can be used as a substitute for the nested loops above. The following uses a dictionary (defaultdict) to hold comparison results for each sublist in a and b, then checks to see if there were matches - it does both the a to b and b to a comparisons.
import itertools, collections
pairs = itertools.product(a, b)
results = collections.defaultdict(list)
for sub_one, sub_two in pairs:
comparison = good_stuff(sub_one) == good_stuff(sub_two)
results[tuple(sub_one)].append(comparison)
results[tuple(sub_two)].append(comparison)
for sublist, comparisons in results.items():
if any(comparisons):
continue
print(sublist)
# or
from pprint import pprint
results = [sublist for sublist, comparisons in results.items() if not any(comparisons)]
pprint(results)

for v in a,b:
for items in v:
if 'NETFLOW02' in items:
print('\t'.join(items))
I'm not sure this is ok for your purpose but you seems to want to capture the results of a network interface called NETFLOW02 from these two lists.
I'm sure there's probably a reason this is unacceptable but you could also expand this to include other keywords in longer lists, well, any length of lists that are nested as far as explained in your question. To do this, you would need to create another list, hypothetically keywords = ['NETFLOW02','ETH01']
Then we simply iterate this list also.
results = []
for v in a,b:
for item in v:
for kw in keywords:
if kw in item:
results.append(item)
print('\t'.join(item))
print(results)

Related

Convert double for loop with break to list comprehension

I have a type something like this:
class T:
id: int
data: Any
I have a list of Ts with unique ids. I am provided another list of Ts with new data. So I want a new list that replaces any of the items where I have a new T with that new T.
current = [T(1), T(2), T(3)]
new = [T(2)*]
output = [T(1), T(2)*, T(3)]
I have a working double for loop with a break that I'm trying to turn into a list comprehension if it's cleaner.
output = []
for item in current_items:
for new_item in new_items:
if new_item.id == item.id:
item = new_item
break
output.append(item)
This is what I tried but without the ability to break the inner loop after performing the if condition it obviously doesn't work.
output = [
new_item if new_item.id == item.id else item
for item in current_items
for new_item in new_items
]
Let's feed next a generator expression finding elements in b with the same id as the current element in a, and default to the current element in a if no matching id is found in b.
>>> from dataclasses import dataclass
>>> #dataclass
... class T(object):
... id: int
... name: str
...
>>> a = [T(1, "foo"), T(2, "bar"), T(3, "baz")]
>>> b = [T(2, "wooble")]
>>> [next((y for y in b if y.id == x.id), x) for x in a]
[T(id=1, name='foo'), T(id=2, name='wooble'), T(id=3, name='baz')]
Using next will replicate the behavior of your loop with a break on finding a match. The entire b list will only be iterated if a match isn't found.
If we didn't care about efficiency, we could generate a list of all matching items in b and then take the first one if it's not empty.
[c[0] if (c := [y for y in b if y.id == x.id]) else x for x in a]
But that both looks uglier and is potentially less efficient both in terms of runtime complexity and space, as it generates a bunch of useless lists.
Basically what you are doing is setting the output to be new_items that are in the current items. I would suggest creating a set of current item ids and then just filtering new items that are in that set.
current_item_ids = {
item.id
for item in current_items
}
output = [
item
for item in new_items
if item in current_item_ids
]

Comparing entries in a list to another list

Using Python 3.9.5
a=['Apple', 'Orange', 'peaches']
b=[['Lettuce', 'Apple', 'eggs'],['potato', 'tomato', 'pepper']]
I want to compare for any values in a to b and if there is a match continue to the next list (
my program generates lists of key words) i want to compare the initial list "a" to the lists i have and if there is a match go next and if there is no match then do something like print that list.
this is what i tried, not working though
for i in b:
if any(x in a for x in [b, c]):
continue
else:
print(#the current sublist)
i would like to say that with integers this code works but with lists or strings it doesn't, appreciate the feedback
a = ['Apple', 'Orange', 'peaches']
b = [['Lettuce', 'Apple', 'eggs'], ['potato', 'tomato', 'pepper']]
for el in b:
if any([x in a for x in el]):
print("ok")
else:
print(el)
Returns:
True
False
Explanation:
First of all we iterate over b, so we have el = ['Lettuce', 'Apple', 'eggs'] on the first iteration.
Next we create list of bools: [x in a for x in el]. We check are there elements of a in our current element el: [False, True, False] - for first element of b.
Next we reduce our list of bools ([False, True, False]) to one bool using any()
That should work if I understand the problem correctly:
to_check = ['Apple','Orange','peaches']
list_of_lists = [['Lettuce', 'Apple','eggs'],['potato','tomato','pepper']]
for _list in list_of_lists:
if any([element_to_check in _list for element_to_check in to_check]):
print('Matched')
else:
print('Not matched')
I don't know where are you are getting the variable c. Just replace this line and it will work.
From:
if any(x in a for x in [b,c]):
To:
if any(x in a for x in i):
The value of i is each sub-list in b such as ['Lettuce', 'Apple', 'eggs'] thus your algorithm iterates each item in the sub-list and checks for any element that is also in a.
Here is an improvement to your algorithm. Currently, your algorithm runs at a time complexity of O(z * y * x) where:
x = length of a
y = length of b
z = average length of each sub-list in b
Instead of always traversing the list a, make it a hash table e.g. set, this will make the searches improve from linear O(x) to constant O(1).
a_set = set(a)
for i in b:
if any(x in a_set for x in i):
# Do something or just continue
continue
else:
print(i)
This will improve the time complexity to O(z * y). As a comparison, if we have 20 elements in a, 10 in b, and an average of 3 in each sub-list of b, the previous algorithm using a list a would run 3 * 10 * 20 for a total of 600 iterations, compare to a set a which would only run 3 * 10 for a total of 30 iterations.

Python: Compare Lists

I have two lists a and b:
a = ['146769015', '163081689', '172235774', ...]
b = [['StackOverflow (146769015)'], ['StackOverflow (146769015)'], ['StackOverflow (163081689)'], ...]
What I'm trying to do is to check if the elements of list a are in list b, and if they are, how many times they appear.
In this case the output should be:
'146769015':2
'163081689':1
I've already tried the set() function but that does not seem to work
print(set(a)&set(b))
And i get this
print(set(a)&set(b))
TypeError: unhashable type: 'list'
Is it possible to do what i want?
Thank you all.
When you perform set(a) & set(b), you're trying to see which elements both lists share. There are a couple errors in your logic.
First, your first list is comprised of strings. Your second list is comprised of lists.
Second, the elements of your second list are never the same than your first list, because the first has only numbers, and the second has numbers and letters.
Third, even if you only extract the numbers, the intersection of both sets will bring which numbers are on both sets, but not how many times.
A good approach might be to extract the numbers in your second list and then count occurrences if they are present in list a:
from collections import Counter
import re
a=['146769015', '163081689', '172235774']
b=[['StackOverflow (146769015)'],['StackOverflow (146769015)'],['StackOverflow (163081689)']]
numbs = [re.search('\d+', elem[0]).group(0) for elem in b]
cnt = Counter()
for n in numbs:
if n in a:
cnt[n]+= 1
Output:
Counter({'146769015': 2, '163081689': 1})
I'll leave as homework to you to research what are dictionaries and Counters.
It's tricky when you have a string as a subset of strings, otherwise I think you could use a Counter from collections and iterate that using a as a key.
Otherwise you can flatten the list and nested loop through it.
from collections import defaultdict
flat_list = [item for sublist in b for item in sublist]
c = defaultdict(lambda: 0)
for string in a:
for string2 in flat_list:
if string in string2:
c[string] += 1
You can use a dictionary:
a=['146769015', '163081689', '172235774']
b=[['StackOverflow (146769015)'],['StackOverflow (146769015)'],['StackOverflow (163081689)']]
c = {}
for s in a:
for d in b:
for i in d:
if s in i:
if s not in c:
c[s] = 1
else:
c[s] += 1
print(c)
Output:
{'146769015': 2, '163081689': 1}

Categorize elements of a list in python

I want to efficiently categorize the elements of a given list L1. This list can be arbitrary long, so I am looking for an efficient way to do the following.
The list L1 contains several elements [e_1,...,e_N] that can be compared with a generic function called areTheSame(e1,e2). If this function returns True, it means that both elements belong to the same category.
At the end, I want to have another list L2, which in turn contains different lists [LC_1, ..., LC_M]. Each LC list contains all the elements from the same category.
Assuming that the function is transitive and reflective (and if it's not, the whole grouping does not seem to make much sense), it is enough to compare each word to one "representative" from each group, e.g. just the first or last element. If no such group exists, create a new group, e.g. using next with an empty list as default element.
lst = "a list with some words with different lengths".split()
areTheSame = lambda x, y: len(x) == len(y)
res = []
for w in lst:
l = next((x for x in res if areTheSame(w, x[0])), [])
if l == []:
res.append(l)
l.append(w)
Result: [['a'], ['list', 'with', 'some', 'with'], ['words'], ['different'], ['lengths']]
Still, this has complexity O(n*k), where n is the number of words and k the number of groups. It would be more efficient if instead of areTheSame(x,y) you had a function getGroup(x), then you'd have O(n). That is, instead of testing whether two elements belong to the same group, that function would extract the attribute(s) that determine which group the element belongs to. In my example, that's just the len of the strings, but in your case it might be more complex.
getGroup = lambda x: len(x)
d = collections.defaultdict(list)
for w in lst:
d[getGroup(w)].append(w)
Result: {1: ['a'], 4: ['list', 'with', 'some', 'with'], 5: ['words'], 9: ['different'], 7: ['lengths']}
I believe you can use itertools groupby function but might need to modify the areTheSame function so it will be a keyfunc, i.e. will yield some kind of key.
L1 = sorted(L1, key=keyfunc)
L2 = [list(g) for _, g in groupby(L1, keyfunc))

How to XOR two lists in Python? [duplicate]

This question already has answers here:
Comparing two lists and only printing the differences? (XORing two lists)
(6 answers)
Closed 2 years ago.
I've got two lists, for example:
a = ['hello','world']
b = ['hello','world','im','steve']
If I want to create a third list that only contains elements NOT in both:
c = ['im','steve']
How do I do this if the order of the elements IS important? I know I can use sets but they keep throwing out the order of my lists. I could use ' '.join(list) to convert them to strings but not sure how to do this operation in that format either.
You can concatenate the lists and use list comprehension:
a = ['hello','world']
b = ['hello','world','im','steve']
final_vals = [i for i in a+b if i not in a or i not in b]
Output:
['im', 'steve']
Option 1: set method (recommended)
Sets have a symmetric_difference method that exclusively return elements from either a or b. Order can be preserved with a list comprehension for a concatenated list a + b.
comp = set(a).symmetric_difference(b)
[x for x in a + b if x in comp]
# ['im', 'steve']
Option 2: pathlib method
For reference, another way to diff two lists might be with pathlib.Path.relative_to method:
import pathlib
p = pathlib.Path(*b)
r = p.relative_to(*a)
list(r.parts)
# ['im', 'steve']
Note: b is the longer list. This option is potentially less efficient than a simple list comprehension.
Add two lists together and minus the intersection part if it shows in the new list. Order is preserved.
c = a + b
for v in set(a).intersection(set(b)):
while v in c:
c.remove(v)
a = ['hello','world']
b = ['hello','world','im','steve']
a = set(a)
b = set(b)
print(a.symmetric_difference(b))
This code print elements that are only in one of the tables.
Look here:
https://learnpython.org/en/Sets
You could also just create a function that filters elements from l1 that don't exist in l2, and call it twice with the arguments flipped:
a = ['hello','world', 'foo']
b = ['hello','world','im','steve']
def difference(l1, l2):
return list(filter(lambda x: x not in l2, l1))
print(difference(a, b) + difference(b, a))
# ['foo', 'im', 'steve']
If you don't wish to use filter(), a simple list comprehension like this also works:
[item for item in l1 if item not in l2]
The question is not very clear, indeed, and probably you're good with #Ajax1234 's answer, but here's another "take" on it.
If you wanna compare positions (kind of what a bit-wise XOR would do) you can do something like getting the shortest list, iterate checking position by position with the longest list (check the same position in the longest list matches the word in the shortest list) and then add the remainder (the "unwalked" part of the longest list). Something like the following:
a = ['hello', 'world']
b = ['hello', 'world', 'im', 'steve']
min_list = a if len(a) < len(b) else b
max_list = b if len(b) > len(a) else a
results = []
for i, item in enumerate(min_list):
# Iterate through the shortest list to avoid IndexError(s)
if min_list[i] != max_list[i]:
results.append(min_list[i])
results.append(max_list[i])
results.extend(max_list[i + 1:])
print(results)
# Prints: ['im', 'steve']
However, then you have the problem of what to do if the same positions don't match. I mean... What to do in that case? In the code above, I just added both entries to the results list, which means for the following inputs:
a = ['hello', 'foo']
b = ['hello', 'world', 'im', 'steve']
would output:
>>> ['foo', 'world', 'im', 'steve']
(notice both foo from list a and world from list b have been added)
Using standard for loop to check for items not in one or the other list (may be more understandable than list comprehension):
a = ['hello','world', 'foo']
b = ['hello','world','im','steve']
c = a+b
ans = []
for i in c:
if i not in a or i not in b:
ans.append(i)
print(ans)
Output:
['foo', 'im', 'steve']
I recommend, using ^ operator with sets, like set(a) ^ set(b), Example (demo):
>>> a = ['hello','world']
>>> b = ['hello','world','im','steve']
>>> set(a) ^ set(b)
{'steve', 'im'}
>>> sorted(set(a) ^ set(b),key=max([a,b],key=len).index)
['im', 'steve']
>>>
https://docs.python.org/2/library/stdtypes.html#frozenset.symmetric_difference

Categories

Resources