How to delete item and his info in a list? - python

I have the following list:
lst= ['Jason', 999999999, 'jason#live.com', 'Curt', 333333333, 'curt#job.com']
I want to delete Jason and the next 2 entries and the following so I´m thinking in this :
for i in range(len(lst)):
if "Jason" in lst:
del lst[0]
del lst[1]
del lst[2]
else:
print("Jason not in lst")
Is this correct?
What I´m working with thanks to Tigerhawk is the following:
Original list:
lst = `[['Curt', 333333333, 'curt#job.com'], ['Jason', 999999999, 'jason#live.com']]`
def clean_lst(lst):
name=str(input("Name you want to delete:")) #With this I get the lst on the 1st paragraph
lst = sum(lst, [])
if len(lst)==0:
print("Empty List")
elif name in lst:
idx = lst.index(name)
del lst[idx:idx+3]
else:
print("Name is not on the list")
End result should look like this:
lst = `[['Curt', 333333333, 'curt#job.com']]`

If you can have more than one, start from the end of the list and del i to i + 3 if l[i] is equal to Jason:
l = ['Jason', 999999999, 'jason#live.com', 'Curt', 333333333, 'curt#job.com', "Jason", "foo", "bar"]
for i in range(len(l) - 1, -1, -1):
if l[i] == "Jason":
del l[i:i+3]
Output:
['Curt', 333333333, 'curt#job.com']
As far as your own code goes, it presumes that "Jason" is always the first element even after removing any previous which seems unlikely but only you know for sure.
The most efficient way to do this, is to either create a new list or update the original using a generator function:
def rem_jas(l):
it = iter(l)
for ele in it:
if ele == "Jason":
# skip two elements
next(it,"")
next(it, "")
else:
yield ele
Output:
In [30]: l = ['Jason', 999999999, 'jason#live.com', 'Curt', 333333333, 'curt#job.com', "Jason", "foo", "bar"]
In [31]: l[:] = rem_jas(l)
In [32]: l
Out[32]: ['Curt', 333333333, 'curt#job.com']
If you can possibly have Jason within two elements of another Jason then you need to decide what is the appropriate thing to do. If there is always at least 3 spaces then it will be fine.
Based on your edit and the fact you have a list of lists not a flat list, it seems you want to remove each sublist where the name appears which makes the code a lost simpler:
lst = [['Curt', 333333333, 'curt#job.com'], ['Jason', 999999999, 'jason#live.com']]
from itertools import chain
lst[:] = chain(*(sub for sub in lst if "Jason" not in sub))
print(lst)
Output:
['Curt', 333333333, 'curt#job.com']
sum is not a good way to flatten a list, itertools.chain is far more efficient.
If you want to keep the sublists then don't flatten:
lst[:] = (sub for sub in lst if "Jason" not in sub)
print(lst)
Or a hybrid if you have multiple Jasons and need to add a few prints based on conditions:
def rem_jas(l, name):
it = iter(l)
for ele in it:
if ele == name:
# skip two elements
next(it,"")
next(it, "")
else:
yield ele
def clean_lst(l):
name = "Jason"
for sub in l:
tmp = list(rem_jas(sub, name))
if tmp:
yield tmp
if len(tmp) == len(sub):
print("{} not in sublist".format(name))
lst[:] = clean_lst(lst)
print(lst)
Demo:
In [5]: lst = [['Curt', 333333333, 'curt#job.com'], ['Jason', 999999999, 'jason#live.com']]
In [6]: lst[:] = clean_lst(lst)
Jason not in sublist
In [7]: print(lst)
[['Curt', 333333333, 'curt#job.com']]
And lastly if you want to let the user know which sublist was missing the name:
def clean_lst(l):
name = "Jason"
for ind, sub in enumerate(l):
tmp = list(rem_jas(sub, name))
if tmp:
yield tmp
if len(tmp) == len(sub):
print("{} not in sublist {}".format(name, ind))

You can simply search for the appropriate index and then delete a slice of three entries:
lst = ['Jason', 999999999, 'jason#live.com', 'Curt', 333333333, 'curt#job.com']
if 'Jason' in lst:
idx = lst.index('Jason')
del lst[idx:idx+3]
Result:
>>> lst
['Curt', 333333333, 'curt#job.com']

Related

python list of lists contain substring

I have the list_of_lists and I need to get the string that contains 'height' in the sublists and if there is no height at all I need to get 'nvt' for the whole sublist.
I have tried the following:
list_of_lists = [['width=9','length=3'],['width=6','length=4','height=4']]
_lists = []
for list in list_of_lists:
list1 = []
for st in list:
if ("height" ) in st:
list1.append(st)
else:
list1.append('nvt')
_lists.append(list1)
OUT = _lists
the result I need to have is :
_lists = ['nvt', 'height=4']
what I'm getting is:
_lists = [['nvt','nvt'],['nvt','nvt','height=4']]
This is a good case for implementing a for/else construct as follows:
list_of_lists = [['width=9','length=3'],['width=6','length=4','height=4']]
result = []
for e in list_of_lists:
for ss in e:
if ss.startswith('height'):
result.append(ss)
break
else:
result.append('nvt')
print(result)
Output:
['nvt', 'height=4']
Note:
This could probably be done with a list comprehension but I think this is more obvious and probably has no significant difference in terms of performance
This should work, you can assign height variable to first value in the sublist where s.startswith("height") is True, and if nothing matches this filter, you can assign height to 'nvt'.
_lists = []
for sublist in list_of_lists:
height = next(filter(lambda s: s.startswith("height"), sublist), 'nvt')
_lists.append(height)
And if you wish to be crazy, you can use list comprehension to reduce the code to the:
_lists = [next(filter(lambda s: s.startswith("height"), sublist), 'nvt') for sublist in list_of_lists]
Try this (Python 3.x):
import re
list_of_lists = [['width=9','length=3'],['width=6','length=4','height=4']]
_lists = []
r = re.compile("height=")
for li in list_of_lists:
match = list(filter(r.match, li))
if len(match) > 0:
_lists.extend(match)
else:
_lists.append('nvt')
OUT = _lists
print(OUT)

Remove short overlapping string from list of string

I have a list of strings: mylist = ["Hanks", "Tom Hanks","Tom","Tom Can"], I need to remove shorter strings that are substring of another string in the list.
For example in the case above, output should be : ["Tom Hanks","Tom Can"].
What I have done in python:
mylist = ["Hanks", "Tom Hanks","Tom","Tom Can"]
newlst = []
for x in mylist:
noexist = True
for j in mylist:
if x==j:continue
noexist = noexist and not(x in j)
if (noexist==True):
newlst.append(x)
print(newlst)
The code works fine. How can I make it efficient?
If order in output does not matter (replace ',' character with a character that doesn't occur in strings of your list):
mylist = ["Hanks", "Tom Hanks","Tom","Tom Can"]
mylist.sort(key = len)
newlst = []
for i,x in enumerate(mylist):
if x not in ','.join(mylist[i+1:]):
newlst.append(x)
list comprehension alternative (less readable):
mylist = ["Hanks", "Tom Hanks","Tom","Tom Can"]
mylist.sort(key = len)
newlst = [x for i,x in enumerate(mylist) if x not in ','.join(mylist[i+1:])]
output:
['Tom Can', 'Tom Hanks']
And if you want to keep the order:
mylist = ["Hanks", "Tom Hanks","Tom","Tom Can"]
mylist_sorted = mylist.copy()
mylist_sorted.sort(key = len)
newlst = [x for i,x in enumerate(mylist_sorted) if x not in ','.join(mylist_sorted[i+1:])]
newlst = [x for x in mylist if x in newlst]
output:
['Tom Hanks', 'Tom Can']
See this can help you. Added answer based on question sample list :
mylist = ["Hanks", "Tom Hanks","Tom","Tom Can"]
newlist = []
newstring = "|".join(mylist)
for a in mylist:
if newstring.count(a) == 1:
print("Big string: ",a)
newlist.append(a)
else:
print("Small String: ",a)
print(newlist)
Added if else print statement how its traverse and check condition.
a pretty minor improvement without changing the overall algorithm is that once you find another element that contains the current element then you can break out of the inner loop since it is skipped after that.
mylist = ["Hanks", "Tom Hanks","Tom","Tom Can"]
newlist = []
for elem in mylist:
for candidate in mylist:
if elem == candidate:
continue
elif elem in candidate:
break
else:
newlist.append(elem)
print(newlist)
If your strings are always words, you can just split on the words and filter by set operations, which should be quite fast.
from collections import Counter
items = ["Hanks", "Tom Hanks","Tom","Tom Can"]
items = set(items) # Don't want to think about uniqueness
item_words = {} # {item: all_words}
word_counts = Counter() # {word: item_counts}
word_lookups = {} # {word: {all_words: {item, ...}, ...}, ...}
for item in items:
words = frozenset(item.split())
item_words[item] = words
for word in words:
word_lookups.setdefault(word, {}).setdefault(words, set()).add(item)
word_counts[word] += 1
def is_ok(item):
words = item_words[item]
min_word = min(words, key=word_counts.__getitem__)
if word_counts[min_word] == 1:
return True # This item has a unique word
for all_words, others in word_lookups[min_word].items():
if not words.issubset(all_words):
continue # Not all words present
for other in others:
if item == other:
continue # Don't remove yourself
if item in other:
return False
return True # No matches
final = [item for item in items if is_ok(item)]
If you want to be very fast, consider a variation on the Aho–Corasick algorithm, where you would construct patterns for all your entries, and match them against all your inputs, and discard any patterns that have more than one match. This could potentially be linear in time.

Filter a list of strings by frequency

I have a list of strings:
a = ['book','book','cards','book','foo','foo','computer']
I want to return anything in this list that's x > 2
Final output:
a = ['book','book','book']
I'm not quite sure how to approach this. But here's two methods I had in mind:
Approach One:
I've created a dictionary to count the number of times an item appears:
a = ['book','book','cards','book','foo','foo','computer']
import collections
def update_item_counts(item_counts, itemset):
for a in itemset:
item_counts[a] +=1
test = defaultdict(int)
update_item_counts(test, a)
print(test)
Out: defaultdict(<class 'int'>, {'book': 3, 'cards': 1, 'foo': 2, 'computer': 1})
I want to filter out the list with this dictionary but I'm not sure how to do that.
Approach two:
I tried to write a list comprehension but it doesn't seem to work:
res = [k for k in a if a.count > 2 in k]
A very barebone answer is that you should replace a.count by a.count(k) in your second solution.
Although, do not attempt to use list.count for this, as this will traverse the list for each item. Instead count occurences first with collections.Counter. This has the advantage of traversing the list only once.
from collections import Counter
from itertools import repeat
a = ['book','book','cards','book','foo','foo','computer']
count = Counter(a)
output = [word for item, n in count.items() if n > 2 for word in repeat(item, n)]
print(output) # ['book', 'book', 'book']
Note that the list comprehension is equivalent to the loop below.
output = []
for item, n in count.items():
if n > 2:
output.extend(repeat(item, n))
Try this:
a_list = ['book','book','cards','book','foo','foo','computer']
b_list = []
for a in a_list:
if a_list.count(a) > 2:
b_list.append(a)
print(b_list)
# ['book', 'book', 'book']
Edit: You mentioned list comprehension. You are on the right track! You can do it with list comprehension like this:
a_list = ['book','book','cards','book','foo','foo','computer']
c_list = [a for a in a_list if a_list.count(a) > 2]
Good luck!
a = ['book','book','cards','book','foo','foo','computer']
list(filter(lambda s: a.count(s) > 2, a))
Your first attempt builds a dictionary with all of the counts. You need to take this a step further to get the items that you want:
res = [k for k in test if test[k] > 2]
Now that you have built this by hand, you should check out the builtin Counter class that does all of the work for you.
If you just want to print there are better answers already, if you want to remove you can try this.
a = ['book','book','cards','book','foo','foo','computer']
countdict = {}
for word in a:
if word not in countdict:
countdict[word] = 1
else:
countdict[word] += 1
for x, y in countdict.items():
if (2 >= y):
for i in range(y):
a.remove(x)
You can try this.
def my_filter(my_list, my_freq):
'''Filter a list of strings by frequency'''
# use set() to unique my_list, then turn set back to list
unique_list = list(set(my_list))
# count frequency in unique_list
frequencies = []
for value in unique_list:
frequencies.append(my_list.count(value))
# filter frequency
return_list = []
for i, frequency in enumerate(frequencies):
if frequency > my_freq:
for _ in range(frequency):
return_list.append(unique_list[i])
return return_list
a = ['book','book','cards','book','foo','foo','computer']
my_filter(a, 2)
['book', 'book', 'book']

How do I pop() from a list without using pop()?

I have a function called pop_item() that I am trying to make act like a pop() that works with the list class, how would I do that, here is my code:
def empty(lst):
return lst == []
def pop_item(lst):
lst = lst[::-1]
new_lst = []
for i in lst:
if i != lst[-1]:
new_lst += [i]
lst = new_lst
return lst
def main():
todo = [1,2,3,4]
print(todo) #prints [1,2,3,4] as expected
print('\n' + 75 * '_' + '\n')
print(pop_item(todo)) #pop the item off
print(todo) #output should then be [1,2,3]
if __name__ == '__main__':
main()
Note: I am not allowed to use any built in functions eg len(),index,del() etc.
Here are a few variants of popping items from a list without using list functions, only slicing & list comprehension
pop the last item using slicing & slicing assignment:
def pop_item(lst):
lst[:] = lst[:-1]
pop a valued item (last one but can be a parameter) by rebuilding the list & slice assign
def pop_item(lst):
lst[:] = [x for x in lst if x!=lst[-1]]
(that can pop other items in the list if they have the same value)
by speciftying item position/index in the list:
def pop_item(lst,item_position):
lst[:] = [x for i,x in enumerate(lst) if i!=item_position]

Nested lists python

Can anyone tell me how can I call for indexes in a nested list?
Generally I just write:
for i in range (list)
but what if I have a list with nested lists as below:
Nlist = [[2,2,2],[3,3,3],[4,4,4]...]
and I want to go through the indexes of each one separately?
If you really need the indices you can just do what you said again for the inner list:
l = [[2,2,2],[3,3,3],[4,4,4]]
for index1 in xrange(len(l)):
for index2 in xrange(len(l[index1])):
print index1, index2, l[index1][index2]
But it is more pythonic to iterate through the list itself:
for inner_l in l:
for item in inner_l:
print item
If you really need the indices you can also use enumerate:
for index1, inner_l in enumerate(l):
for index2, item in enumerate(inner_l):
print index1, index2, item, l[index1][index2]
Try this setup:
a = [["a","b","c",],["d","e"],["f","g","h"]]
To print the 2nd element in the 1st list ("b"), use print a[0][1] - For the 2nd element in 3rd list ("g"): print a[2][1]
The first brackets reference which nested list you're accessing, the second pair references the item in that list.
You can do this. Adapt it to your situation:
for l in Nlist:
for item in l:
print item
The question title is too wide and the author's need is more specific. In my case, I needed to extract all elements from nested list like in the example below:
Example:
input -> [1,2,[3,4]]
output -> [1,2,3,4]
The code below gives me the result, but I would like to know if anyone can create a simpler answer:
def get_elements_from_nested_list(l, new_l):
if l is not None:
e = l[0]
if isinstance(e, list):
get_elements_from_nested_list(e, new_l)
else:
new_l.append(e)
if len(l) > 1:
return get_elements_from_nested_list(l[1:], new_l)
else:
return new_l
Call of the method
l = [1,2,[3,4]]
new_l = []
get_elements_from_nested_list(l, new_l)
n = [[1, 2, 3], [4, 5, 6, 7, 8, 9]]
def flatten(lists):
results = []
for numbers in lists:
for numbers2 in numbers:
results.append(numbers2)
return results
print flatten(n)
Output: n = [1,2,3,4,5,6,7,8,9]
I think you want to access list values and their indices simultaneously and separately:
l = [[2,2,2],[3,3,3],[4,4,4],[5,5,5]]
l_len = len(l)
l_item_len = len(l[0])
for i in range(l_len):
for j in range(l_item_len):
print(f'List[{i}][{j}] : {l[i][j]}' )

Categories

Resources