My question is to write a function which returns the longest string and ignores any non-strings, and if there are no strings in the input list, then it should return None.
my answer:
def longest_string(x):
for i in max(x, key=len):
if not type(i)==str:
continue
if
return max
longest_string(['cat', 'dog', 'horse'])
I'm a beginner so I have no idea where to start. Apologies if this is quite simple.
This is how i would do it:
def longest_string(x):
Strings = [i for i in x if isinstance(i, str)]
return(max(Strings, key=len)) if Strings else None
Based on your code:
def longest_string(x):
l = 0
r = None
for s in x:
if isinstance(s, str) and len(s) > l:
l = len(s)
r = s
return r
print(longest_string([None, 'cat', 1, 'dog', 'horse']))
# horse
def longest_string(items):
try:
return max([x for x in items if isinstance(x, str)], key=len)
except ValueError:
return None
def longest_string(items):
strings = (s for s in items if isinstance(s, str))
longest = max(strings, key=len) if strings else None
return longest
print(longest_string(['cat', 'dog', 'horse']))
Your syntax is wrong (second-to-last line: if with no condition) and you are returning max which you did not define manually. In actuality, max is a built-in Python function which you called a few lines above.
In addition, you are not looping through all strings, you are looping through the longest string. Your code should instead be
def longest_string(l):
strings = [item for item in l if type(item) == str]
if len(strings):
return max(strings, key=len)
return None
You're on a good way, you could iterate the list and check each item is the longest:
def longest_string(x)
# handle case of 0 strings
if len(x) == 0:
return None
current_longest = ""
# Iterate the strings
for i in x:
# Handle nonestring
if type(i) != str:
continue
# if the current string is longer than the longest, replace the string.
if len(i) > len(current_longest):
current_longest = i
# This condition handles multiple elements where none are strings and should return None.
if len(current_longest) > 0:
return current_longest
else:
return None
Since you are a beginner, I recommend you to start using python's built-in methods to sort and manage lists. Is the best when it comes to logic and leaves less room for bugs.
def longest_string(x):
x = filter(lambda obj: isinstance(obj, str), x)
longest = max(list(x), key=lambda obj: len(obj), default=None)
return longest
Nonetheless, you were in a good way. Just avoid using python´s keywords for variable names (such as max, type, list, etc.)
EDIT: I see a lot of answers using one-liner conditionals, list comprehension, etc. I think those are fantastic solutions, but for the level of programming the OP is at, my answer attempts to document each step of the process and be as readable as possible.
First of all, I would highly suggest defining the type of the x argument in your function.
For example; since I see you are passing a list, you can define the type like so:
def longest_string(x: list):
....
This not only makes it more readable for potential collaborators but helps enormously when creating docstrings and/or combined with using an IDE that shows type hints when writing functions.
Next, I highly suggest you break down your "specs" into some pseudocode, which is enormously helpful for taking things one step at a time:
returns the longest string
ignores any non-strings
if there are no strings in the input list, then it should return None.
So to elaborate on those "specifications" further, we can write:
Return the longest string from a list.
Ignore any element from the input arg x that is not of type str
if no string is present in the list, return None
From here we can proceed to writing the function.
def longest_string(x: list):
# Immediately verify the input is the expected type. if not, return None (or raise Exception)
if type(x) != list:
return None # input should always be a list
# create an empty list to add all strings to
str_list = []
# Loop through list
for element in x:
# check type. if not string, continue
if type(element) != str:
pass
# at this point in our loop the element has passed our type check, and is a string.
# add the element to our str_list
str_list.append(element)
# we should now have a list of strings
# however we should handle an edge case where a list is passed to the function that contains no strings at all, which would mean we now have an empty str_list. let's check that
if not str_list: # an empty list evaluates to False. if not str_list is basically saying "if str_list is empty"
return None
# if the program has not hit one of the return statements yet, we should now have a list of strings (or at least 1 string). you can check with a simple print statement (eg. print(str_list), print(len(str_list)) )
# now we can check for the longest string
# we can use the max() function for this operation
longest_string = max(str_list, key=len)
# return the longest string!
return longest_string
Related
Given a string, find the first non-repeating character in it and return its index. If it doesn't exist, return -1. Input string already all lowercase.
Why does my code not work?
str1 = input("give me a string: ")
def unique(x):
stack = []
if x is None:
return (-1)
i = 0
while i < len(x):
stack = stack.append(x[i])
if x[i] in stack:
return(i)
else:
i += 1
unique(str1)
str1 = input("give me a string: ")
def unique(x):
for i in x:
if x.count(i) == 1:
return x.index(i)
else:
return -1
print(unique(str1))
This will work
Explanation
Instead of using the list stack use the count() function of the string. The function unique(x) will return the index of that first element whose count is 1 in the str1 string.
You need to know what your code is doing to figure out why it doesn't work, let's breakthrough it step by step.
you create a empty list stack for later use, that's fine.
if x is None is a strange way to check if a string is given, and it doesn't work because even a empty string "" is not equal to None. is is used to check if both sides are the same object, and == is a better operator to check if values of both sides are the same. Therefore, if x == "" is better, but if not x is even better to check if something is empty.
using variable i and while loop to iterate the string is fine.
append() change the list in-place and return None, so stack = stack.append(x[i]) is assigning None to stack.
in stack is going to raise TypeError as NoneType is not iterable. If we change the last line to stack.append(x[i]), it now works, as x[0] is already appended to stack, if x[0] in stack must be True and return 0 for your result.
That's what your code is doing, you just append the first character and return the first index. You need to go through the whole string to know if a character is unique.
Although Rishabh's answer is cleaner, I provide a way to doing it using lists to save seen and repeated characters, then read the string again to find the index of unique character.
x = input("give me a string: ")
def unique(x):
seen = []
repeated = []
for char in x:
if char in seen:
repeated.append(char)
else:
seen.append(char)
for idx, char in enumerate(x):
if char not in repeated:
return idx
return -1
print(unique(x))
I have this exercise:
Write a recursive function that takes a string and returns all the characters that are not repeated in said string.
The characters in the output don't need to have the same order as in the input string.
First I tried this, but given the condition for the function to stop, it never evaluates the last character:
i=0
lst = []
def list_of_letters_rec(str=""):
if str[i] not in lst and i < len(str) - 1:
lst.append(str[i])
list_of_letters_rec(str[i+1:])
elif str[i] in lst and i < len(str) - 1:
list_of_letters_rec(str[i+1:])
elif i > len(str) - 1:
return lst
return lst
word = input(str("Word?"))
print(list_of_letters_rec(word))
The main issue with this function is that it never evaluates the last character.
An example of an output:
['a', 'r', 'd', 'v'] for input 'aardvark'.
Since the characters don't need to be ordered, I suppose a better approach would be to do the recursion backwards, and I also tried another approach (below), but no luck:
lst = []
def list_of_letters_rec(str=""):
n = len(str) - 1
if str[n] not in lst and n >= 0:
lst.append(str[n])
list_of_letters_rec(str[:n-1])
elif str[n] in lst and n >= 0:
list_of_letters_rec(str[:n-1])
return lst
word = input(str("Word?"))
print(list_of_letters_rec(word))
Apparently, the stop conditions are not well defined, especially in the last one, as the output I get is
IndexError: string index out of range
Could you give me any hints to help me correct the stop condition, either in the 1st or 2nd try?
You can try:
word = input("> ")
result = [l for l in word if word.count(l) < 2]
> aabc
['b', 'c']
Demo
One improvement I would offer on #trincot's answer is the use of a set, which has better look-up time, O(1), compared to lists, O(n).
if the input string, s, is empty, return the empty result
(inductive) s has at least one character. if the first character, s[0] is in the memo, mem, the character has already been seen. Return the result of the sub-problem, s[1:]
(inductive) The first character is not in the memo. Add the first character to the memo and prepend the first character to the result of the sub-problem, s[1:]
def list_of_letters(s, mem = set()):
if not s:
return "" #1
elif s[0] in mem:
return list_of_letters(s[1:], mem) #2
else:
return s[0] + list_of_letters(s[1:], {*mem, s[0]}) #3
print(list_of_letters("aardvark"))
ardvk
Per your comment, the exercise asks only for a string as input. We can easily modify our program to privatize mem -
def list_of_letters(s): # public api
def loop(s, mem): # private api
if not s:
return ""
elif s[0] in mem:
return loop(s[1:], mem)
else:
return s[0] + loop(s[1:], {*mem, s[0]})
return loop(s, set()) # run private func
print(list_of_letters("aardvark")) # mem is invisible to caller
ardvk
Python's native set data type accepts an iterable which solves this problem instantly. However this doesn't teach you anything about recursion :D
print("".join(set("aardvark")))
akdrv
Some issues:
You miss the last character because of i < len(str) - 1 in the conditionals. That should be i < len(str) (but read the next points, as this still needs change)
The test for if i > len(str) - 1 should come first, before doing anything else, otherwise you'll get an invalid index reference. This also makes the other conditions on the length unnecessary.
Don't name your variable str, as that is already a used name for the string type.
Don't populate a list that is global. By doing this, you can only call the function once reliably. Any next time the list will still have the result of the previous call, and you'll be adding to that. Instead use the list that you get from the recursive call. In the base case, return an empty list.
The global i has no use, since you never change its value; it is always 0. So you should just reference index [0] and check that the string is not empty.
Here is your code with those corrections:
def list_of_letters_rec(s=""):
if not s:
return []
result = list_of_letters_rec(s[1:])
if s[0] not in result:
result.append(s[0])
return result
print(list_of_letters_rec("aardvark"))
NB: This is not the most optimal way to do it. But I guess this is what you are asked to do.
A possible solution would be to just use an index instead of splicing the string:
def list_of_letters_rec(string="", index = 0, lst = []):
if(len(string) == index):
return lst
char = string[index]
if string.count(char) == 1:
lst.append(char)
return list_of_letters_rec(string, index+1, lst)
word = input(str("Word?"))
print(list_of_letters_rec(word))
I have to write a function that takes a list and char gets a list with all instances of the char removed.
I'm just not quite getting anywhere with the sublists. I've been programming in Java for some time but I'm new to python.
My code:
def my_remove(the_char, the_list):
if the_list == []:#works
return the_list
if isinstance(the_list[0],list): #if the first element in the list is, itself, a list
#remove the character from the first element/list and move on to other list elements in the main list
else:
print 'else'
return the_list
print 'regular return'
return the_list
The key to this recursive problem (and most) is understanding mathematical induction -
def my_remove(the_char, the_list):
if the_list == []: #1
return the_list
elif isinstance(the_list[0],list): #2
return my_remove(the_char, the_list[0]) \
+ my_remove(the_char, the_list[1:])
elif the_list[0] == the_char: #3
return my_remove(the_char, the_list[1:])
else: #4
return [ the_list[0] ] + my_remove(the_char, the_list[1:])
input = ['a','z',['z','b',['c','z','z']],[['d']],'z']
print(my_remove('z', input))
# ['a','b','c','d']
In the numbered comments -
terminating condition and base case: when the input is empty, return an empty output
otherwise, by induction, the list is not empty. If the first element of the list is another list, combine the result of calling my_remove on the first element and my_remove on the tail of the list, list[1:]
otherwise, by induction, the list is not empty and the first element of the list is not a list. If the first element matches the char, simply call my_remove on the tail of the list.
otherwise, by induction, the list is not empty and the first element is of the list is not a list, and the first element of the list does not match the char. Include the first element of the list in the output and combine it with my_remove called on the tail of the list
Your question explicitly says that helper functions cannot be used. This is most likely an indication of a bad programming teacher. Helper functions make it possible to remove complexity from your program, thereby gaining a complexity-free mind.
Given some generic functions for working on lists -
def isEmpty(l):
return len(l) == 0
def isList(l):
return isinstance(l, list)
def head(l):
return l[0]
def tail(l):
return l[1:]
We can write my_remove with a richer semantics that immediately communicates its intentions to the reader -
def my_remove(x, lst):
if isEmpty(lst):
return lst
elif isList(head(lst)):
return my_remove(x, head(lst)) \
+ my_remove(x, tail(lst))
elif head(lst) == x:
return my_remove(x, tail(lst))
else:
return [ head(lst) ] + my_remove(x, tail(lst))
The output is the same, of course -
print(my_remove('z', input))
# ['a','b','c','d']
These helpers could be further improved to protect the programmer for their misuse. Ie, raising a RuntimeWarning is a good way to let you know you made a mistake with your inductive reasoning -
def isEmpty(l):
return isList(l) and len(l) == 0
def isList(l):
return isinstance(l, list)
def head(l):
if isEmpty(l):
raise RuntimeWarning('head called on empty list')
else:
return l[0]
def tail(l):
if isEmpty(l):
raise RuntimeWarning('tail called on empty list')
else:
return l[1:]
There are two cases to handle:
when word is list
when word is str
For case 1, when word is an empty list (base case) return an empty list.
Return the concatenated list of a list of the function invoked with the first item in the list and the result of the function invoked with the remaining items in the list.
For case 2, when the word is an empty string (base case) return an empty string,
when the first character of the word matched the character searched for, return the result of calling the recursive function with the rest of the string.
Otherwise, concatenate the first character together with the result of calling the recursive function with the rest of the string.
import doctest
def remove(word, char):
"""
>>> remove([['swan'], ['elephant'], 'snake'], 'e')
[['swan'], ['lphant'], 'snak']
>>> remove(['swan', [['elephant']], ['snake']], 'e')
['swan', [['lphant']], ['snak']]
"""
if isinstance(word, list):
if word == []:
return []
head, tail = word[0], word[1:]
return [remove(head, char)] + remove(tail, char)
if isinstance(word, str):
if word == '':
return ''
head, tail = word[0], word[1:]
if head == char:
return remove(tail, char)
return head + remove(tail, char)
doctest.testmod()
The purpose of this code is to find the longest string in alphabetical order that occurs first and return that subset.
I can execute the code once, but when I try to loop it I get 'NoneType' object is not iterable (points to last line). I have made sure that what I return and input are all not of NoneType, so I feel like I'm missing a fundamental.
This is my first project in the class, so the code doesn't need to be the "best" or most efficient way - it's just about learning the basics at this point.
s = 'efghiabcdefg'
best = ''
comp = ''
temp = ''
def prog(comp, temp, best, s):
for char in s:
if comp <= char: #Begins COMParison of first CHARacter to <null>
comp = char #If the following character is larger (alphabetical), stores that as the next value to compare to.
temp = temp + comp #Creates a TEMPorary string of characters in alpha order.
if len(temp) > len(best): #Accepts first string as longest string, then compares subsequent strings to the "best" length string, replacing if longer.
best = temp
if len(best) == len(s): #This is the code that was added...
return(s, best) #...to fix the problem.
else:
s = s.lstrip(temp) #Removes those characters considered in this pass
return (str(s), str(best)) #Provides new input for subsequent passes
while len(s) != 0:
(s, best) = prog(comp, temp, best, s)
prog is returning None. The error you get is when you try to unpack the result into the tuple (s, best)
You need to fix your logic so that prog is guaranteed to not return None. It will return None if your code never executes the else clause in the loop.
You don't return in all cases. In Python, if a function ends without an explicit return statement, it will return None.
Consider returning something if, for example, the input string is empty.
I have written a function to check for the existence of a value in a list and return True if it exists. It works well for exact matches, but I need for it to return True if the value exists anywhere in the list entry (e.g. value <= listEntry, I think.) Here is the code I am using for the function:
def isValInLst(val,lst):
"""check to see if val is in lst. If it doesn't NOT exist (i.e. != 0),
return True. Otherwise return false."""
if lst.count(val) != 0:
return True
else:
print 'val is '+str(val)
return False
Without looping through the entire character string and/or using RegEx's (unless those are the most efficient), how should I go about this in a pythonic manner?
This is very similar to another SO question, but I need to check for the existence of the ENTIRE val string anywhere in the list. It would also be great to return the index / indices of matches, but I'm sure that's covered elsewhere on Stackoverflow.
If I understood your question then I guess you need any:
return any(val in x for x in lst)
Demo:
>>> lst = ['aaa','dfbbsd','sdfdee']
>>> val = 'bb'
>>> any(val in x for x in lst)
True
>>> val = "foo"
>>> any(val in x for x in lst)
False
>>> val = "fde"
>>> any(val in x for x in lst)
True
Mostly covered, but if you want to get the index of the matches I would suggest something like this:
indices = [index for index, content in enumerate(input) if substring in content]
if you want to add in the true/false you can still directly use the result from this list comprehension since it will return an empty list if your input doesn't contain the substring which will evaluate to False.
In the terms of your first function:
def isValInLst(val, lst):
return bool([index for index, content in enumerate(lst) if val in content])
where the bool() just converts the answer into a boolean value, but without the bool this will return a list of all places where the substring appears in the list.
There are multiple possibilities to do that. For example:
def valInList1 (val, lst):
# check `in` for each element in the list
return any(val in x for x in lst)
def valInList2 (val, lst):
# join the list to a single string using some character
# that definitely does not occur in val
return val in ';;;'.join(lst)