Making a new list in alphabetical order - python

My python instructions are: Write a function called merge
takes two lists of strings that are each in alphabetical order.
The function should return a list that has all the strings
in alphabetical order.
The function should return a list that has all the strings in alphabetical
order.
For example, if the function were given the two lists
[“cat”, “dog”, “pat”] and [ “bat”, “hamster”, “piglet”, “worm”]
it should return the list
[“bat”, “cat”, “dog “, hamster”, “pat”, “piglet”, “worm”].
Roughly speaking you will start with an empty list to hold the merged list
and an index set to 0 at the beginning of each list.
Compare the first words. Whichever comes first in alphabetical
order gets appended to the merged list and the index of that list
is increased. Continue until one of the lists is empty and copy
the remainder of the other list to the merged list.
Right now I have the code
list1 = ["cat", "dog", "pat"]
list2 = [ "bat", "hamster", "piglet", "worm"]
def merge (list1, list2):
newlist = []
newlist = list1 +list2
final = sorted(newlist)
return final
print merge (list1, list2)
It works, but it's not following instructions. I'm not really sure how to do it with comparing the two lists then appending them to the new list. I also didn't do it with an index set to 0. Can anyone help tweak my code so it fits the instructions?

lets start by writing a compare function
def cmp_lists(L1,L2):
'''returns the first item of the lesser list and the remaining lists'''
if L1 < L2:
return L1[0],(L1[1:],L2)
return L2[0],(L1,L2[1:])
now write a method to merge 2 lists
def merge(L1,L2):
if not L1: #if L1 is empty return L2
return L2
elif not L2: #if L2 is empty return L1
return L1
#otherwise take the first item from one of the lists
next_itm,(L1,L2) = cmp_lists(L1,L2)
# and recursively carry on
return [next_itm] + merge(L1,L2)

What you have been instructed is to write an implementation for a simple merge function that is popularly used in merge-sort sorting algorithm.
Since both lists are already sorted, you don't need to join and sort them again. Rather, just loop until one of the lists is empty and keep comparing the first elements of both lists. Pop the shorter element and add to the newer list.
Keep doing so until one or both of the lists become empty. If one of the lists have extra elements remaining, add all of them to the new list.
I encourage you to write the code once again.

It's a merge sort? Oh that's not tough. Just reverse both lists, compare each pair, and pop the loser. Once either list is empty, the remaining list is sorted in ascending order compared to the entire result, so just concatenate it.
from operator import itemgetter as iget
def merge(lst1, lst2):
lst1, lst2 = lst1[::-1], lst2[::-1]
# popping from the end is MASSIVELY better than popping from the front
result = []
while True:
if not all([lst1, lst2]): # one list is empty:
return result + lst1[::-1] + lst2[::-1] # so add them both and return
lst_to_pop_from = min(lst1,lst2, key=iget(-1))
# gets the list with the smallest final element
result.append(lst_to_pop_from.pop())
# DEMO
In [53]: list1 = ["cat", "dog", "pat"]
In [54]: list2 = [ "bat", "hamster", "piglet", "worm"]
In [55]: merge(list1, list2)
Out[55]: ['bat', 'cat', 'dog', 'hamster', 'pat', 'piglet', 'worm']

Merge sort works by looking at the top elements of the list and putting them in the newly created list.
def merge(list1, list2): # assume input lists are sorted
iter1 = iter(list1) # iterator on the list
iter2 = iter(list2)
top1 = iter1.next() # first element of the list
top2 = iter2.next()
newlist = [] # new list to fill in
while True: # loop will exit with break statement
if top1 <= top2:
newlist.append(top1) # put smaller element in new list
try:
top1 = iter1.next() # try to get next element in list1
except StopIteration:
newlist.append(top2) # if not, fill list with top2
newlist.extend(list(iter2)) # and rest of list2
break # and exit loop
else:
newlist.append(top2)
try:
top2 = iter2.next()
except StopIteration:
newlist.append(top1) # if not, fill list with top1
newlist.extend(list(iter1)) # and rest of list1
break
return newlist

Related

Common items in list of lists

I have a list of lists, and I want to make a function that checks if each of the lists inside have exactly one item in common with all the other lists, if so return True.
Couldn't make it work, is there a simple way to do it without using modules?
I've tried something like this:
list_of_lists = [['d','b','s'],['e','b','f'],['s','f','l'],['b','l','t']]
new_list = []
for i in list_of_lists:
for item in i:
new_list.append(item)
if len(set(new_list)) == len(new_list)-len(list_of_lists):
return True
if you want to intersect all the items in the sublist you can convert them to a set and find intersection check if its an empty set.
list_of_lists = [['d','b','s'],['e','b','f'],['s','f','l'],['b','l','t']]
common_items = set.intersection(*[set(_) for _ in list_of_lists])
if len(common_items) == 1:
return True
return False
Using list comprehension:
def func(list_of_lists):
return sum([all([item in lst for lst in list_of_lists[1:]]) for item in list_of_lists[0]]) == 1
Works if each is guaranteed for one of each item. Also if its not, returns True only if there is one match.
use the Counter after joining a list and a compare list to determine occurrences. Ensure at least one item in the resulting list has a frequency of 2.
from collections import Counter
list_of_lists = [['d','b','s'],['e','b','f'],['s','f','l'],['b','l','t']]
for i in range(len(list_of_lists)):
for j in range(i+1,len(list_of_lists)):
result=(list_of_lists[i]+list_of_lists[j])
counts=(Counter(result))
matches={x for k,x in counts.items() if x==2}
if len(matches)==0:
print("missing a match")

find common unique elements of lists and returning None if empty list

Why the last part of my code doesn't work?
def common_elements(list1, list2):
#a list with common element
common_elements=[]
common_unique_elements=[]
for i in list1:
if i in list2:
common_elements.append(i)
#removing repeated elements
for i in common_elements:
if i not in common_unique_elements:
common_unique_elements.append(i)
#returning None for empty list
if len(common_elements)==0:
return None
return sorted(common_unique_elements)
There is absolutely no need of a second list just to hold unique common elements. You can do the check before you add to first list, thus avoiding the need of second list:
def common_elements(list1, list2):
common = []
for i in list1:
if i in list2 and i not in common:
common.append(i)
return None if not common else sorted(common)
list1 = ['a','b','b,','c']
list2 = ['f','b','e','a','a']
print(common_elements(list1, list2))
# ['a', 'b']
Avoid using function name for variables; can run into problems later.

Can you use AND in List comprehension conditional statements?

I am trying to use List Comprehension to perform the following. I want to make a new list (unique) that only has the common numbers from both lists.
unique = []
for listcomp in range(len(list1)):
if list1[listcomp] in list2 and list1[listcomp] not in unique:
unique.append(list1[listcomp])
else:
continue
Above works fine but when I create the List comprehension below I get duplicates if list1 has duplicate numbers. i.e. list1 = [1, 1, 2], list2 = [1, 5]. I created my list comprehension as
unique = [list1[listcomp] for listcomp in range(len(list1)) if list1[listcomp] in list2 and list1[listcomp] not in unique]
If I'm getting duplicates I assume the "and" statement isn't being applied? I have read other queries about moving the if statement further up the comprehension statement but this didn't work. Can you use AND to extend your conditions?
Many thanks
My full code is:-
import random as rnd
# Randomly generate the size of your list
list1size = rnd.randint(1,20)
list2size = rnd.randint(1,20)
# Declare your list variables
list1 = []
list2 = []
# Fill your lists with randomly generated numbers upto the listsize generated above
for x in range(list1size):
list1.append(rnd.randint(1,15))
for y in range(list2size):
list2.append(rnd.randint(1,15))
# Not required but easier to read lists once sorted
list1.sort()
list2.sort()
print(list1)
print(list2)
# Now to compare old school
unique = []
# for listcomp in range(len(list1)):
# if list1[listcomp] in list2 and list1[listcomp] not in unique:
# unique.append(list1[listcomp])
# else:
# continue
# Now to compare with list comprehension
unique = [list1[listcomp] for listcomp in range(len(list1)) if list1[listcomp] in list2 and list1[listcomp] not in unique]
# Above doesn't stop duplicates if they are in List1 so I assume you can't use AND
print(f"The common numbers in both lists are {unique}")
You can't access elements produced by a list comprehension as you go along. Your condition list1[listcomp] not in unique will always return True since at that moment in time unique is defined as the empty list intialised in unique = [].
So the and statement is being applied, but not the in way you want.
Instead, you can create a "seen" set holding items you have already found and omit them. The standard implementation is found in the itertools unique_everseen recipe.
If you have the 3rd party toolz library, you can use the identical toolz.unique and feed a generator expression. More Pythonic, you can iterate elements directly rather than using indices:
from toolz import unique
unique = list(unique(i for i in list1 if i in list2))

Culling certain numbers from a List

I have a series of variable lists inside a list, and I'm comparing it to another list. I want, run through each list in aList, analyze each number and as soon its a match in bList, append that number to finalList. I want to return the first match in other words and ignore future matches. For Example:
aList = [[0,1],[8,9,4,5],[7,6,3,2]]
bList = [0,5,1,4]
finalList = [0,4]
Use a for loop with a break.
finalList = []
for sl in aList:
for item in sl:
if item in bList:
finalList.append(item)
break
To iterate with a single for loop you could use the itertools module

Randomly chose an element of one list that's NOT in a second list

Say I have a list2 of randomly chosen elements from a large list1. Is there a clever way of choosing an element from list1 that's NOT already in list2?
For example:
list1 = range(20,100)
list2 = [37,49,22,35,72] # could be much longer
while True:
n = random.choice(list1)
if n not in list2:
break
# now n is an element of list1 that's not in list2
I feel like there must be a more efficient way of doing this than a guess-and-check while-loop.
You can subtract list2 of list1:
list3 = list(set(list1)-set(list2))
and choose from it randomly:
random.choice(list3)
Note: you need to reconvert the set to a list.
You could use:
import random
list1 = range(20,100)
list2 = [37,49,22,35,72]
not_in_list2 = [item for item in list1 if item not in list2]
n = random.choice(not_in_list2)
This uses a list comprehension to create a list of all elements in list1 that aren't inlist2. It then selects randomly from this list. Unlike when working with sets, this technique does not change the probability of items being selected, because it does not remove duplicate elements from list1.
In case that there are no repeating elements in list1, this is a pythonic way, working with set and -:
import random
list1 = range(20,100)
list2 = [37,49,22,35,72] # could be much longer
n = random.choice(tuple(set(list1)-set(list2)))
# now n is an element of list1 that's not in list2
The tuple call is needed to avoid a NotIndexable exception.
If you want to randomly select more than one item from a list, or select an item from a set, it's better to use random.sample instead of choice
import random
diff = set(list1)-set(list2)
num_to_select = 1 # set the number to select here.
list_of_random_items = random.sample(diff, num_to_select)
If you do not want the overhead of creating a new list (or a new list and two sets) which can become quite costly if list1 is very large, there is another option.
import random
list1 = range(20,100)
list2 = [37,49,22,35,72]
for i in list2:
while i in list1:
list1.remove(i)
random.choice(list1)
Just iterate through the items in list2 and remove them from list1. Since list.remove() only removes the first occurrence of an item, I added a while-loop to ensure that all occurences are removed.

Categories

Resources