Python function removing items form list, dropping unexpected elements - python-3.x

It's a simple code practice challenge that asks that I make a function that takes a list of mixed types and returns only the integers
def return_only_integer(lst):
for i in lst:
if type(i) != int:
lst.remove(i)
return lst
That's it, it seems simple enough but the tests are coming back negative:
return_only_integer([9, 2, "space", "car", "lion", 16])
Returns: [9, 2, 'car', 16]
return_only_integer(["hello", 81, "basketball", 123, "fox"])
Returns what it should: [81, 123]
return_only_integer([10, "121", 56, 20, "car", 3, "lion"])
Also returns what it should: [10, 56, 20, 3]
but:
return_only_integer(["String", True, 3.3, 1])
Returns: [True, 1]
The code is so simple and straightforward, I have no idea why these 2 tests are failing.
Why would 'car' even be in the first list but the other strings not?
type(True) is bool, why is it there?

This is probably due to you modifying the list in the conditional. By removing an item from the list, you are likely shifting the iteration in that operation.
It may be worth looking into filter() instead.
https://docs.python.org/3/library/functions.html#filter

You can create a temporary list inside your function to hold the items that are integers. Once you have processed all the items, you can return the temporary list as part of your return statement. If there are no integers, you can return None.
def return_only_integer(lst):
int_lst = []
for i in lst:
if type(i) == int:
int_lst.append(i)
return int_lst if int_lst else None
print (return_only_integer([9, 2, "space", "car", "lion", 16]))
print (return_only_integer(['ball', True, "space", "car", "lion", 'fish']))
This will output as follows:
[9, 2, 16]
None

def return_only_integer(lst):
for i in lst:
if type(i) != int:
lst.remove(i)
return lst
THIS FUNCTION HAS VERY BIG FAULT.
consider this list [9, 2, "space", "car", "lion", 16]
when it had removed "space" then your i had directly reached to lion and it ignored car because your list is changed and your i index is not changed. so it is moving as it is.
after removing one non integer, you must make sure to change the index position of i. so try this code. it will work.
def return_only_integer(lst):
for i in lst:
#print(i,type(i))
if type(i) != int:
#print("flag this is not integer ",i)
lst.remove(i)
return_only_integer(lst)
return(lst)
print(return_only_integer(["hello", 81, "basketball", 123, "fox"]))
hope u understand. if you didn't understand then tell me .

Indeed you should not change the list you are iterating on, it produces unexpected results. Item deleted leaves room for the next, without the latter being picked in the iteration (so not being dropped in your example).
The possible choices to perform such task involve the usage of another list. A possible solution is very familiar to python developers:
def return_only_integer(lst):
return [i for i in lst if type(i) == int]

Related

The del function in the for loop removes every second element. I want to delete all of them until the condition is met

a = [0,0,0,0,0,0,0,0,0,1,2,3,4,0,0]
for i in a:
if i < 3:
del a[0]
print(a)
[0, 0, 0, 1, 2, 3, 4]
should be:
[3,4,0,0]
You are using i in a confusing way, since you're not using it as an index (which is kinda the norm) but as an iterator. Just for readability I would suggest changing the iterator name to something more descriptive.
If I understand you're question correctly, you want to delete the first element, until it is bigger or equal 3. If that is your question, you could do it like this:
a = [0,0,0,0,0,0,0,0,0,1,2,3,4,0,0]
while a[0]<3:
del a[0]
print(a) # [3,4,0,0]
a = [0,0,0,0,0,0,0,0,0,1,2,3,4]
n= []
for i in range(len(a)):
if(a[i]<3):
continue
else:
n.append(a[i])
print(n) # [3,4]

How to get all keys and values in list of Nested dictionaries?

di = [{ "k": [1, 3, 5], "k1": { "k10" : 4, "k11": [4, 7, 9], "k12" : { "k120" : { "k121" : "v121" }}, "k14" : 6}}, {"k22": { "k221" : "v122"}}]
key_list = list()
val_list = list()
for i in di:
key_list.extend(i.keys())
val_list.extend(i.values())
for i in val_list:
if "dict" in str(type(i)):
key_list.extend(i.keys())
val_list.remove(i)
val_list.extend(i.values())
for i in val_list:
if "dict" in str(type(i)):
key_list.extend(i.keys())
val_list.remove(i)
val_list.extend(i.values())
print("Key list: ",key_list)
print("Vlaue list: ", val_list)
"""This is giving my answer but i need a optimised way and also for N nested dictionary how to get all keys and values, I need get all keys in a list and all values in a list."""
About optimization: If there is a reason of existing hierarchy there is not "optimised way" to disrespect the hierarchy, otherwise a real optimization needs to include a change not to construct the hierarchy that would be disrespected later.
I wrote the following, is not optimized but decently efficient and concise, it does something in the direction of what you imprecisely asked: (I let you refine what you intend to be returned, I noticed that you silently excluded to return those value whose type is dictionary, I didn't make such discrimination). To help to understand the flexibility of the code I made some examples as cases controlled by the behavioral variable bkv, this behavioral variable can be squeezed when you know what you want to obtain.
dictype=type({})
listype=type([])
def through(ref,bkv):
""" bkv in binary 01=1 yield key
10=2 yield value
11=3 yield (key,value) """
if type(ref)==listype:
for x in ref:
yield from through(x,bkv)
elif type(ref)==dictype:
for kv in ref.items():
if bkv==1:
yield kv[0]
elif bkv==2:
yield kv[1]
elif bkv==3:
yield kv
else:
throw(TypeError, "Not implemented")
yield from through(kv[1],bkv)
di = [
{ "k": [1, 3, 5],
"k1": { "k10" : 4,
"k11": [4, 7, 9],
"k12" : { "k120" : { "k121" : "v121" }},
"k14" : 6}},
{"k22": { "k221" : "v122"}}
]
list_keys=[x for x in through(di,1)]
list_values=[x for x in through(di,2)]
list_keyval=[x for x in through(di,3)]
print("Key list: ",list_keys)
print("Values list (all, not excluding dict values): ",list_values)
print("all (Key,Value) pairs:", list_keyval)

Python defining iterators for dictionary comprehension for a class

I want to check with someone if I defined the iteration function properly. To explain, please consider the following example:
x=[{'n':'foo', 'a': [1,2,3], 'b':[2,3,5]}, {'n':'baz','a':[4,5,6], 'b':[7,8,9]},
{'n':'foo', 'a': [4,3,4], 'b':[1,5,6]}, {'n':'bar','a':[1,2,2], 'b':[2,5,6]}]
quick_dict = {key['n']: [sample['a'] for sample in x if sample['n']==key['n']] for key in x}
This works as expected and outputs:
{'foo': [[1, 2, 3], [4, 3, 4]], 'baz': [[4, 5, 6]], 'bar': [[1, 2, 2]]}
I am trying to do something similar for a class I defined using the __next__ and __iter__ methods. The class instance has many functions and attributes but for the purpose of this question, only the attribute samples is important because it is a list of dictionaries exactly as in the above example. I defined the methods as follows:
def __next__(self):
if self.itercounter < len(self.samples)-1:
self.itercounter +=1
return self.samples[self.itercounter]
else:
raise StopIteration
def __iter__(self):
self.itercounter = -1
return self
This seems to work for list comprehensions, but it fails for dictionary comprehensions.
If I do:
quick_dict = {key['Name']: [sample['CM'] for sample in data if sample['Name'] == key['Name']]
for key in data.samples}
then it works because it is directly accessing the list of dictionaries and it knows what to do. On the other hand if I do
quick_dict = {key['Name']: [sample['CM'] for sample in data if sample['Name'] == key['Name']]
for key in data}
then it is going through my functions, and it doesn't work. It just returns a dictionary with a single key. Here 'CM' is just a key like 'a' in the example.
What am I doing wrong in my definition of __iter__ and __next__?
Your second definition of quick_dict iterates over data with for sample in data while already iterating over it with for key in data. However, your __iter__ and __next__ implementation uses a single instance attribute to control iteration, meaning that nested iteration over data won't work because the second (nested) call to __iter__ resets the counter. To support nested iteration, eliminate __next__ and have __iter__ return a generator instead:
def __iter__(self):
i = -1
while i < len(self.samples)-1:
i += 1
yield self.samples[i]

Get a list of all number in a certain range containing only certain digits without checking each number

Is there a way to create a list of all numbers less than 10,000 that do not contain any of the digits 0, 2, 4, 5, 6, 8? Of course one can simply type something like:
bads = ['0', '2', '4', '5', '6', '8']
goods = []
for n in range(1, 10000, 2):
if not any(bad in str(n) for bad in bads):
goods.append(n)
However, I'm looking for a method which instead considers the digits 1, 3, 7, 9 and creates all possible unique strings of permutations of these numbers of size 4 or less, duplicate digits allowed. Does itertools, for example, have something that would easily do this? I looked at the permutations method, but that doesn't produce numbers with repeated digits from the collection, and the product method doesn't seem to be what I'm after either, given that it simply would return Cartesian products of 1, 3, 5, 7 with itself.
Here's a simple-minded approach using permutations and combinations_with_replacement from itertools:
from itertools import permutations, combinations_with_replacement
def digit_combinations(power_of_ten):
numbers = set()
for n in range(1, power_of_ten + 1):
for combination in combinations_with_replacement("1379", n):
numbers |= set(permutations(combination, len(combination)))
return sorted(int(''.join(number)) for number in numbers)
print(digit_combinations(4))
OUTPUT
[1, 3, 7, 9, 11, 13, 17, 19, ..., 9971, 9973, 9977, 9979, 9991, 9993, 9997, 9999]
It could be made more space efficient using generators, but depending on the range, it might not be worth it. (For up to 10,000 there are only 340 numbers.) For numbers to 10^4, this code takes roughly as long as your simple example. But for 10^7, this code runs over 40x faster on my system than your simple example.
Could you include your idea for the generator?
Here's a basic rework of the code above into generator form:
from itertools import permutations, combinations_with_replacement
def digit_combinations_generator(power_of_ten):
for n in range(1, power_of_ten + 1):
for combination in combinations_with_replacement("1379", n):
for number in set(permutations(combination, len(combination))):
yield int(''.join(number))
generator = digit_combinations_generator(4)
while True:
try:
print(next(generator), end=', ')
except StopIteration:
print()
break
This does not return the numbers sorted, it just hands them out as fast as it generates them.

Fast bit string generation given indices

Here a is a list, for example [34, 52, 57].
The function takes in this list and creates a bit string of length 64, where every index is a 0 except at the given indices.
So it would look like [0,0,....1,...1,..1,..0,0,0] where only at indices [34, 52, 57] we have ones.
def bit_string_gen(a):
bit_string = []
for key, value in enumerate(range(64)):
if key in a:
bit_string.append(1)
else:
bit_string.append(0)
return bit_string
Is there a better way to do this, maybe using lambda or map or itertools instead of enumerate.
The problem with your approach is that you:
use an if statement for every bit; and
use the in test which can be rather expensive.
A beter method can be:
def bit_string_gen(a):
bit_string = [0]*64
for value in a:
bit_string[value] = 1
return bit_string
So here you iterate only over the values of a, and you set these bits to 1.
Nevertheless it is a bit weird to encode this with a list of ints. A more compact way to do this is to encode this binary on an integer. For instance by using:
def bit_string_gen(a):
bit_string = 0
for value in a:
bit_string |= 1 << value
return bit_string
So in the last case if you set the bits like in your sample input, you obtain:
>>> bin(bit_string_gen([34, 52, 57]))
'0b1000010000000000000000010000000000000000000000000000000000'
>>> hex(bit_string_gen([34, 52, 57]))
'0x210000400000000'
If you're looking for a solution using map/lambda, here's a one-liner:
map(lambda x: 1 if x in [34, 52, 57] else 0, range(0, 64))

Resources