How to convert a nested if statement into a lambda function (python) - python

For a recent Python homework assignment, we were assigned to create a function that would return words in a list that start with 'd'. Here's the relevant code:
def filter_words(word_list, letter):
'''
INPUT: list of words, string
OUTPUT: list of words
Return the words from word_list which start with the given letter.
Example:
>>> filter_words(["salumeria", "dandelion", "yamo", "doc loi", "rosamunde",
"beretta", "ike's", "delfina"], "d")
['dandelion', 'doc loi', 'delfina']
'''
letter_list = []
for word in word_list:
if word[0] == letter:
letter_list.append(word)
return letter_list
The above nested if statement that I wrote works when I run the code, which I'm happy about (:D); however, in trying to become more pythonic and astute with the language, I found a very helpful article on why Lambda functions are useful and how to possibly solve this same challenge with a lambda, but I couldn't figure out how to make it work in this case.
I'm asking for guidance on how to potentially write my above nested if statement as a lambda function.

In a way, the lambda equivalent of your if condition would be:
fn = lambda x: x[0] == 'd' #fn("dog") => True, fn("test") => False
Further, you can use .startswith(..) instead of comparing [0]. It then becomes something like:
letter_list = filter(lambda x: x.startswith('d'), word_list)
But more pythonic is:
letter_list = [x for x in word_list if x.startswith('d')]

I'm not sure what you're asking, because changing the if into a lambda of some sort doesn't seem to be useful. You neglected to post your failed code so we'd know what you want.
However, there is a succinct way to express what you're doing:
def filter_words(word_list, letter):
return [word in letter_list if word[0] == letter]

Related

how can I optimize this code? fliping all the vowels in a string

This code takes all the vowels in a string and flip thier positions.
class Solution:
def reverseVowels(self, s: str) -> str:
list_vowels= [x for x in s if x in 'aeiouAEIOU'][::-1]
list=[x for x in s]
z=0
for number in range(0,len(s)):
if list[number] in 'aeiouAEIOU':
list[number]=list_vowels[z]
z+=1
return "".join(list)
I'm still a beginner but I have this feeling that if I submit this code in an interview for exemple, I won't be taken seriously.
Thanks to who ever took time to help me.
The code can be simplified as follows.
use reverse iterator reversed to get successive elements of vowels in reverse order as needed
Use Python PEP 8 Style Guide for naming functions and variables
Making use of a 'constant' rather than defining vowels multiple times (aids code maintainability)
In Python it's highly discouraged to use builtin functions as variable names (e.g. list)
Not clear why the solution uses a method rather than a standalone function but kept as posted (looks like a Leetcode answer template).
Code
class Solution:
def reverse_vowels(self, s: str, VOWELS: str = 'aeiouAEIOU') -> str:
# iterator with vowels reverse
rev_vowels = reversed([c for c in s if c in VOWELS])
# Choose character for non-vowles, elese choose next element of reverse iterator for vowel
return ''.join(c if not c in VOWELS else next(rev_vowels) for c in s)
Test
Sol = Solution()
for t in ['aeio', 'hello']:
print(f'{t} -> {Sol.reverse_vowels(t)}')
Output
aeio -> oiea
hello -> holle

Remove words from a list that end with a suffix without using endswith()

I want to write a python function that takes 2 parameters:
List of words and
Ending letters
I want my function to work in such a way that it modifies the original list of words and removes the words which end with the "ending letters" specified.
For example:
list_words = ["hello", "jello","whatsup","right", "cello", "estello"]
ending = "ello"
my_func(list_words, ending)
This should give the following output:
list_words = ["whatsup","right"]
It should pop off all the strings that end with the ending letters given in the second argument of the function.
I can code this function using the .endswith method but I am not allowed to use it. How else can I do this using a loop?
Try:
def my_func(list_words, ending):
return [word for word in list_words if word[len(word)-len(ending):] != ending]
def filter_words(list_words, ending):
return [*filter(lambda x: x[-len(ending):] != ending , list_words)]
Not allowed to use endswith? Not a problem :-P
def my_func(list_words, ending):
list_words[:] = [word for word in list_words
if not word[::-1].startswith(ending[::-1])]
return list_words
Loopholes ftw.
(Adapted to your insistence on modifying the given list. You should probably really decide whether to modify or return, though, not do both, which is rather unusual in Python.)
You can easily check for the last4 characters of a string using string[-4:].
So you can use the below code
list_words = ["hello", "jello","whatsup","right", "cello", "estello"]
ending = "ello"
def my_func(wordsArray, endingStr):
endLen = len(endingStr)
output = []
for x in wordsArray:
if not x[-endLen:] == endingStr:
output.append(x)
return output
list_words = my_func(list_words, ending)
You can shorten the function with some list comprehension like this:
def short_func(wordsArray, endingStr):
endLen = len(endingStr)
output = [x for x in wordsArray if x[-endLen:] != endingStr]
return output
list_words = short_func(list_words, ending)
It is always better to not modify the existing list you can get a list which doesn't have the words with the ending specified like below. If you want to have it as a function you can have it in a following manner. You can assign the formatted list to list_words again.
def format_list(words, ending):
new_list = []
n = len(ending)
for word in words:
if len(word) >= n and n > 0:
if not word[-n:] == ending:
new_list.append(word)
else:
new_list.append(word)
return new_list
list_words = format_list(list_words, ending)
print(list_words)

How can I use a one-line with map() to turn all vowels in a string upper case?

I want to use map() to turn all vowels in a string into upper case letter; here is my code:
def swap_vowel_case(st):
listt = "AEIOUaeiou"
return "".join(list(map(lambda x: x.upper(), vowel) for vowel in listt))
This does not work.
Intuitively, I want to write:
return "".join(list(map(lambda x: x.upper() for x in listt,st)))
But this doesn't work either. Any ideas how to modify either of the above? Thanks
Add condition when appending
def swap_vowel_case(text):
vowels = "AEIOUaeiou"
return "".join(map(lambda char: char.upper() if char in vowels else char, text))
a = swap_vowel_case("But this doesn't work either. Any ideas how to modify either of the above? Thanks")
print(a)
Output
BUt thIs dOEsn't wOrk EIthEr. Any IdEAs hOw tO mOdIfy EIthEr Of thE AbOvE? ThAnks

Can I call a function inside a Lambda expression in python

I have a function with including if, else condition and for loop. I want to write this function inside a lambda expression. I tried from many ways to create this lambda function. But still I couldn't do it. This is my function with another rules.
negation ='no,not,never'.split(',')
list2 = 'miss,loss,gone,give up,lost'.split(',')
def f(sentence):
s = sentence.split()
l = [s.index(word) for word in s if word in list2]
# Will returns list of indices (of sentence) where word is in list2
if len(l) > 0:
for e in l:
# Check previous word
if s[e-1] not in negation:
print 'sad'
Can I express this function inside a lambda expression since I developing a rule based classifier for detect emotion from a sentence like happy, sad, angry. Following is my lambda function.
rules = [(lambda x: word_tokenize(x)[-1] == '?', "neutral"),
(lambda x: word_tokenize(x)[0] in question, "neutral"),
(lambda x: any(word in list2 for word in [WordNetLemmatizer().lemmatize(word,'v') for word in word_tokenize(x)]), "sad"),
(lambda x: any(word in list1 for word in [WordNetLemmatizer().lemmatize(word,'v') for word in word_tokenize(x)]), "happy")]
print classify("I miss you", rules)
Instead of cramming everything into a lambda expression, I would just create a function that did everything you need it to do (from your comment, it sounds like you want to apply certain rules to a sentence in a certain order). You can always use that function in list comprehension, map, reduce, etc. Since I don't know exactly what your rules are though, this is the best example I can give:
a = ["This is not a sentence. That was false.",
"You cannot play volleyball. You can play baseball.",
"My uncle once ate an entire bag of corn chips! I am not lying!"]
def f(paragraph):
sentences = paragraph.split(".")
result = []
for i in range(len(sentences)):
//apply rules to sentences
if "not" in sentences[i]:
result.append("negative")
else:
result.append("positive")
return result
my_result = [f(x) for x in a]
Your function could use some improvement:
negation_words = {"no", "not", "never"}
sad_words = {"miss", "loss", "gone", "give", "lost"}
def count_occurrences(s, search_words, negation_words=negation_words):
count = 0
neg = False
for word in s.lower().split(): # should also strip punctuation
if word in search_words and not neg:
count += 1
neg = word in negation_words
return count
print("\n".join(["sad"] * count_occurrences(s, sad_words)))

How can I simplify this conversion from underscore to camelcase in Python?

I have written the function below that converts underscore to camelcase with first word in lowercase, i.e. "get_this_value" -> "getThisValue". Also I have requirement to preserve leading and trailing underscores and also double (triple etc.) underscores, if any, i.e.
"_get__this_value_" -> "_get_ThisValue_".
The code:
def underscore_to_camelcase(value):
output = ""
first_word_passed = False
for word in value.split("_"):
if not word:
output += "_"
continue
if first_word_passed:
output += word.capitalize()
else:
output += word.lower()
first_word_passed = True
return output
I am feeling the code above as written in non-Pythonic style, though it works as expected, so looking how to simplify the code and write it using list comprehensions etc.
This one works except for leaving the first word as lowercase.
def convert(word):
return ''.join(x.capitalize() or '_' for x in word.split('_'))
(I know this isn't exactly what you asked for, and this thread is quite old, but since it's quite prominent when searching for such conversions on Google I thought I'd add my solution in case it helps anyone else).
Your code is fine. The problem I think you're trying to solve is that if first_word_passed looks a little bit ugly.
One option for fixing this is a generator. We can easily make this return one thing for first entry and another for all subsequent entries. As Python has first-class functions we can get the generator to return the function we want to use to process each word.
We then just need to use the conditional operator so we can handle the blank entries returned by double underscores within a list comprehension.
So if we have a word we call the generator to get the function to use to set the case, and if we don't we just use _ leaving the generator untouched.
def underscore_to_camelcase(value):
def camelcase():
yield str.lower
while True:
yield str.capitalize
c = camelcase()
return "".join(c.next()(x) if x else '_' for x in value.split("_"))
I prefer a regular expression, personally. Here's one that is doing the trick for me:
import re
def to_camelcase(s):
return re.sub(r'(?!^)_([a-zA-Z])', lambda m: m.group(1).upper(), s)
Using unutbu's tests:
tests = [('get__this_value', 'get_ThisValue'),
('_get__this_value', '_get_ThisValue'),
('_get__this_value_', '_get_ThisValue_'),
('get_this_value', 'getThisValue'),
('get__this__value', 'get_This_Value')]
for test, expected in tests:
assert to_camelcase(test) == expected
Here's a simpler one. Might not be perfect for all situations, but it meets my requirements, since I'm just converting python variables, which have a specific format, to camel-case. This does capitalize all but the first word.
def underscore_to_camelcase(text):
"""
Converts underscore_delimited_text to camelCase.
Useful for JSON output
"""
return ''.join(word.title() if i else word for i, word in enumerate(text.split('_')))
I think the code is fine. You've got a fairly complex specification, so if you insist on squashing it into the Procrustean bed of a list comprehension, then you're likely to harm the clarity of the code.
The only changes I'd make would be:
To use the join method to build the result in O(n) space and time, rather than repeated applications of += which is O(n²).
To add a docstring.
Like this:
def underscore_to_camelcase(s):
"""Take the underscore-separated string s and return a camelCase
equivalent. Initial and final underscores are preserved, and medial
pairs of underscores are turned into a single underscore."""
def camelcase_words(words):
first_word_passed = False
for word in words:
if not word:
yield "_"
continue
if first_word_passed:
yield word.capitalize()
else:
yield word.lower()
first_word_passed = True
return ''.join(camelcase_words(s.split('_')))
Depending on the application, another change I would consider making would be to memoize the function. I presume you're automatically translating source code in some way, and you expect the same names to occur many times. So you might as well store the conversion instead of re-computing it each time. An easy way to do that would be to use the #memoized decorator from the Python decorator library.
This algorithm performs well with digit:
import re
PATTERN = re.compile(r'''
(?<!\A) # not at the start of the string
_
(?=[a-zA-Z]) # followed by a letter
''', re.X)
def camelize(value):
tokens = PATTERN.split(value)
response = tokens.pop(0).lower()
for remain in tokens:
response += remain.capitalize()
return response
Examples:
>>> camelize('Foo')
'foo'
>>> camelize('_Foo')
'_foo'
>>> camelize('Foo_')
'foo_'
>>> camelize('Foo_Bar')
'fooBar'
>>> camelize('Foo__Bar')
'foo_Bar'
>>> camelize('9')
'9'
>>> camelize('9_foo')
'9Foo'
>>> camelize('foo_9')
'foo_9'
>>> camelize('foo_9_bar')
'foo_9Bar'
>>> camelize('foo__9__bar')
'foo__9_Bar'
Here's mine, relying mainly on list comprehension, split, and join. Plus optional parameter to use different delimiter:
def underscore_to_camel(in_str, delim="_"):
chunks = in_str.split(delim)
chunks[1:] = [_.title() for _ in chunks[1:]]
return "".join(chunks)
Also, for sake of completeness, including what was referenced earlier as solution from another question as the reverse (NOT my own code, just repeating for easy reference):
first_cap_re = re.compile('(.)([A-Z][a-z]+)')
all_cap_re = re.compile('([a-z0-9])([A-Z])')
def camel_to_underscore(in_str):
s1 = first_cap_re.sub(r'\1_\2', name)
return all_cap_re.sub(r'\1_\2', s1).lower()
I agree with Gareth that the code is ok. However, if you really want a shorter, yet readable approach you could try something like this:
def underscore_to_camelcase(value):
# Make a list of capitalized words and underscores to be preserved
capitalized_words = [w.capitalize() if w else '_' for w in value.split('_')]
# Convert the first word to lowercase
for i, word in enumerate(capitalized_words):
if word != '_':
capitalized_words[i] = word.lower()
break
# Join all words to a single string and return it
return "".join(capitalized_words)
The problem calls for a function that returns a lowercase word the first time, but capitalized words afterwards. You can do that with an if clause, but then the if clause has to be evaluated for every word. An appealing alternative is to use a generator. It can return one thing on the first call, and something else on successive calls, and it does not require as many ifs.
def lower_camelcase(seq):
it=iter(seq)
for word in it:
yield word.lower()
if word.isalnum(): break
for word in it:
yield word.capitalize()
def underscore_to_camelcase(text):
return ''.join(lower_camelcase(word if word else '_' for word in text.split('_')))
Here is some test code to show that it works:
tests=[('get__this_value','get_ThisValue'),
('_get__this_value','_get_ThisValue'),
('_get__this_value_','_get_ThisValue_'),
('get_this_value','getThisValue'),
('get__this__value','get_This_Value'),
]
for test,answer in tests:
result=underscore_to_camelcase(test)
try:
assert result==answer
except AssertionError:
print('{r!r} != {a!r}'.format(r=result,a=answer))
Here is a list comprehension style generator expression.
from itertools import count
def underscore_to_camelcase(value):
words = value.split('_')
counter = count()
return ''.join('_' if w == '' else w.capitalize() if counter.next() else w for w in words )
def convert(word):
if not isinstance(word, str):
return word
if word.startswith("_"):
word = word[1:]
words = word.split("_")
_words = []
for idx, _word in enumerate(words):
if idx == 0:
_words.append(_word)
continue
_words.append(_word.capitalize())
return ''.join(_words)
This is the most compact way to do it:
def underscore_to_camelcase(value):
words = [word.capitalize() for word in value.split('_')]
words[0]=words[0].lower()
return "".join(words)
Another regexp solution:
import re
def conv(s):
"""Convert underscore-separated strings to camelCase equivalents.
>>> conv('get')
'get'
>>> conv('_get')
'_get'
>>> conv('get_this_value')
'getThisValue'
>>> conv('__get__this_value_')
'_get_ThisValue_'
>>> conv('_get__this_value__')
'_get_ThisValue_'
>>> conv('___get_this_value')
'_getThisValue'
"""
# convert case:
s = re.sub(r'(_*[A-Z])', lambda m: m.group(1).lower(), s.title(), count=1)
# remove/normalize underscores:
s = re.sub(r'__+|^_+|_+$', '|', s).replace('_', '').replace('|', '_')
return s
if __name__ == "__main__":
import doctest
doctest.testmod()
It works for your examples, but it might fail for names containting digits - it depends how you would capitalize them.
For regexp sake !
import re
def underscore_to_camelcase(value):
def rep(m):
if m.group(1) != None:
return m.group(2) + m.group(3).lower() + '_'
else:
return m.group(3).capitalize()
ret, nb_repl = re.subn(r'(^)?(_*)([a-zA-Z]+)', rep, value)
return ret if (nb_repl > 1) else ret[:-1]
A slightly modified version:
import re
def underscore_to_camelcase(value):
first = True
res = []
for u,w in re.findall('([_]*)([^_]*)',value):
if first:
res.append(u+w)
first = False
elif len(w)==0: # trailing underscores
res.append(u)
else: # trim an underscore and capitalize
res.append(u[:-1] + w.title())
return ''.join(res)
I know this has already been answered, but I came up with some syntactic sugar that handles a special case that the selected answer does not (words with dunders in them i.e. "my_word__is_____ugly" to "myWordIsUgly"). Obviously this can be broken up into multiple lines but I liked the challenge of getting it on one. I added line breaks for clarity.
def underscore_to_camel(in_string):
return "".join(
list(
map(
lambda index_word:
index_word[1].lower() if index_word[0] == 0
else index_word[1][0].upper() + (index_word[1][1:] if len(index_word[1]) > 0 else ""),
list(enumerate(re.split(re.compile(r"_+"), in_string)
)
)
)
)
)
Maybe, pydash works for this purpose (https://pydash.readthedocs.io/en/latest/)
>>> from pydash.strings import snake_case
>>>> snake_case('needToBeSnakeCased')
'get__this_value'
>>> from pydash.strings import camel_case
>>>camel_case('_get__this_value_')
'getThisValue'

Categories