pyhamcrest - Compare two list - python

I've just started learning python. Currently writing a unit test to assert if the elements in the expected list is present in the actual list
def test_compare_list_of_items():
actual_list_of_items = ['a','b']
expected_list_of_items = ['a']
assert_that(actual_list_of_items, has_item(has_items(expected_list_of_items)))
but i'm getting errors like
E Expected: a sequence containing (a sequence containing <['a']>)
E but: was <['a', 'b']>
How and what sequence matcher should i use in order to assert if item 'a' in the expected list is present in the actual list?

You are using has_item when you should only be using has_items. According to the docs this takes multiple matchers which is what you want. Your function then becomes
def test_compare_list_of_items():
actual_list_of_items = ['a','b']
expected_list_of_items = ['a']
assert_that(actual_list_of_items, has_items(*expected_list_of_items))
We use iterable unpacking for the list to feed as the arguments and now when you run it, it shouldn't error out.

I don't know about has_items function, but can you just use something like this?
assertTrue(all(item in expected_list_of_items for item in actual_list_of_items))

Related

Unable to store values in an array from for loop in python

First I tried directly storing values from a list having the name 'data' in an array variable 'c' using loop but 'none' got printed
for i in data:
print(i['name'])
c=i['name']
Here print(i['name']) perfectly worked and output appeared
This is the working ouput
Then I printed c in order to print the values generated using loop. The ouput came as none.
print(c)
Then I tried another way by storing the values and making the array iterable at the same time using for loop. An error occurred which I was unable to resolve.
for i in data:
b[c]=i['name']
c=c+1
The error apeared is as follow-
I have tried two ways, if there is any other way please help me out as I am new to python.
It looks like the variable 'data' is a dictionary.
If you want to add each name from that dictionary to a list:
# create a new list variable
names = []
for i in data:
name = i['name']
print(name)
# add the name to the list
names.append(name)
# output the new list
print(names)
Assuming your data object here is a list like [{"name": "Mr. Green", ...}, {"name": "Mr. Blue", ...}].
If your goal is to end up with c == ["Mr. Green", "Mr. Blue"], then you're looking for something like:
c = []
for i in data:
c.append(i['name'])
print(c)
or you can accomplish tasks like these using list comprehensions like:
c = [i['name'] for i in data]
print(c)
The first code example you posted is iterating through the items in data and reassigning the value of c to each item's name key - not adding them to a list("array"). Without knowing more about the code you ran to produce the screenshot and/or the contents of data, it's hard to say why you're seeing print(c) produce None. I'd guess the last item in data is something like {"name": None, ...} which if it's coming from JSON is possible if the value is null. Small note: I'd generally use .get("name") here instead so that your program doesn't blow up if an item is missing a "name" key entirely.
For your second code example, the error is different but I think falls along a similar logical fallacy which is that lists in python function differently from primitives(things like numbers and strings). For the interpreter to know that b or c are supposed to be lists("arrays"), they need to be instantiated differently and they have their own set of syntax/methods for mutation. For example, like arrays in other languages, lists are indexed by position so doing b[c] = <something> will only work if c is an integer. So something similar to your second example that would also produce a list of names like my above would be:
b = [None] * len(data)
c = 0
for i in data:
b[c]=i['name']
c=c+1
Note that if you only initialize b = [], you get an IndexError: list assignment index out of range on the initial assignment of b[0] = "some name" because the list is of size 0.
Add
b = []
above your first line of code. As the error is saying that you have not (and correctly so) defined the list to append.
I personally would use list comprehension here
b = [obj['name'] for obj in data]
where obj is i as you have defined it.

Python: Index slicing from a list for each index in for loop

I got stuck in slicing from a list of data inside a for loop.
list = ['[init.svc.logd]: [running]', '[init.svc.logd-reinit]: [stopped]']
what I am looking for is to print only key without it values (running/stopped)
Overall code,
for each in list:
print(each[:]) #not really sure what may work here
result expected:
init.svc.logd
anyone for a quick solution?
If you want print only the key, you could use the split function to take whatever is before : and then replace [ and ] with nothing if you don't want them:
list = ['[init.svc.logd]: [running]', '[init.svc.logd-reinit]: [stopped]']
for each in list:
print(each.split(":")[0].replace('[','').replace(']','')) #not really sure what may work here
which gives :
init.svc.logd
init.svc.logd-reinit
You should probably be using a regular expression. The concept of 'key' in the question is ambiguous as there are no data constructs shown that have keys - it's merely a list of strings. So...
import re
list_ = ['[init.svc.logd]: [running]', '[init.svc.logd-reinit]: [stopped]']
for e in list_:
if r := re.findall('\[(.*?)\]', e):
print(r[0])
Output:
init.svc.logd
init.svc.logd-reinit
Note:
This is more robust than string splitting solutions for cases where data are unexpectedly malformed

python set union operation is not behaving well with named tuples

I want to create a set of namedtuple in python, with the ability to add elements dynamically using the union operation.
The following code snippet creates a set of namedtuple, which is behaving nicely.
from collections import namedtuple
B = namedtuple('B', 'name x')
b1 = B('b1',90)
b2 = B('b2',92)
s = set([b1,b2])
print(s)
which prints
{B(name='b1', x=90), B(name='b2', x=92)}
Now if I create another namedtuple and add it to my set with the union operations it is not behaving as expected.
b3 = B('b3',93)
s = s.union(b3)
print(s)
The code snippet prints the following output.
{93, B(name='b1', x=90), B(name='b2', x=92), 'b3'}
The expected output should be:
{B(name='b1', x=90), B(name='b2', x=92), B(name='b3', x=93)}
Am I mis-understanding the API?
Both python2 and 3 are showing the same behaviour.
A namedtuple instance is an iterable of items. set.union simply merges the current set with the items in the namedtuple.
However, what you want is put the namedtuple in another container/iterable, so the merge is done with the item (the namedtuple) contained in the new parent iterable:
s.union((b3,))
It becomes more obvious if you actually think of the operator equivalent:
s = s | set(b3) # set(b3) -> {93, 'b3'}
As compared to what we actually want:
s = s | {b3}
The union is performed with the outer iterable.
union expects a set (or a list or another iterable), but you pass a named tuple, which is an iterable by itself, but it provides values, so you merge the set with the values. Try this:
s = s.union({b3})
Since b3 is iterable, union works on its elements rather than on the tuple itself. Replace that with:
s = s.union([b3])
The documentation on set.union actually explains this:
union(*others)
Return a new set with elements from the set and all others.
So it will create a new set including all elements from the others:
>>> set(b3) # these are the unique elements in your `b3`
{93, 'b3'}
>>> s.union(b3) # the union of the unique elements in "s" and "b3"
{B(name='b1', x=90), 93, 'b3', B(name='b2', x=92)}
In your case (because you assign it back to s) you can simply add the item thus avoiding creating a new set entirely:
>>> s.add(b3)
>>> s
{B(name='b1', x=90), B(name='b3', x=93), B(name='b2', x=92)}

Python 'NoneType' itterable in list operation

I am writing a function to remove duplication in one list. For specific:
input : a list
output: a list without any duplication
this is my code:
def remove_duplicate(s_list):
for member in s_list:
for other in s_list.remove(member):
if other == member:
s_list.remove(other)
return s_list
After that, I try to run remove_duplicate([1,2,3,4]) and the error is
for other in s_list.remove(member):
TypeError: 'NoneType' object is not iterable
I would suggest to work with two lists instead of changing one list in-place, if possible.
If order is not important, you can use a set:
dup_list = [1,1,2,3,2,5,2]
dupfree_list = list(set(dup_list))
Otherwise, if the order of elements is important, you can note which elements you have already seen:
dupfree_list = []
for e in dup_list:
if e not in dupfree_list:
dupfree_list.append(e)
I think the most Pythonic way is to use a set which removes duplicates:
listwithduplicates = [1,2,3,3]
listwithduplicates = set(listwithduplicates)
The result is:
{2,1,3}
Note that sets are not ordered the way lists are.

Python list-of-list idioms

data2['C'] is a list that looks like:
[['20120524', '26.31'], ['20120523', '26.08'], ['20120522', '26.52'], ['20120521', '25.75']]
I want to write code that can iterate across d['C'][i][0] and find the list index corresponding to a specific value (a date stored as YYYYMMDD). I still find Python's for loop idioms confusing, and would like clarification on why the first block of code is invalid but the second block of code is valid. Also, if there is a built-in Python function that will do the search procedure I described, please share.
Doesn't work:
>>> print data2['C'][i][0] for i in range(len(data2['C']))
File "<stdin>", line 1
print data2['C'][i][0] for i in range(len(data2['C']))
^
SyntaxError: invalid syntax
Works:
>>> for i in range(len(data2['C'])):
... print data2['C'][i][0]
...
20120524
20120523
20120522
20120521
for can be used in two ways: in an explicit for loop (like your second code block) or in a list/dict/generator comprehension (docs):
print [data2['C'][i][0] for i in range(len(data2['C']))]
Note the brackets. [] means list, {} - a dict and () with for inside - a generator expression.
To have the output identical to the second one, do
print '\n'.join([str(data2['C'][i][0]) for i in range(len(data2['C']))])
or even
print '\n'.join(str(data2['C'][i][0]) for i in range(len(data2['C'])))
This will be a generator expression.
The problem is the first code is a bad list comprehension but the second is a bad iterator. The for in python is a iterator, so, you can do this:
for i in data2['C']:
print i[0]
or
dates = [i[0] for i in data2['C']]
print dates

Categories

Resources