Way to accomplish task using list comphrehension - python

Is there a way to accomplish the following using a list comprehension? Or is there a more Pythonic way of accomplishing this?
count = 0
x = 'uewoiquewqoiuinkcnsjk'
for letter in x:
if letter in ['a', 'e', 'i', 'o', 'u']:
count += 1
Just trying to learn the best programming practices?

Since in generates a True or False and True and False can reliably be used as 1 and 0 you can use sum with a generator:
sum(c in 'aeiou' for c in x)
Or filter + len:
len(filter(lambda c: c in 'aeiou', x))
A great way to do the opposite is to use str.translate to delete characters in the string:
>>> x.translate(None, 'aeiou')
wqwqnkcnsjk
So then you can do:
len(x)-len(x.translate(None, 'aeiou'))
In all cases, the answer is 10

Use the combination of list_comprehension and len function.
>>> x = 'uewoiquewqoiuinkcnsjk'
>>> len([i for i in x if i in 'aeiou'])
10
>>>

Related

How to get count of non-repeating values in list

I know I can do something like below to get number of occurrences of elements in the list:
from collections import Counter
words = ['a', 'b', 'c', 'a']
Counter(words).keys() # equals to list(set(words))
Counter(words).values() # counts the elements' frequency
Outputs:
['a', 'c', 'b']
[2, 1, 1]
But I want to get the count 2 for b and c as b and c occur exactly once in the list.
Is there any way to do this in concise / pythonic way without using Counter or even using above output from Counter?
You could just make an algorithm that does that, here is a one liner (thanks #d.b):
sum(x for x in Counter(words).values() if x == 1)
Or more than one line:
seen = []
count = 0
for word in words:
if word not in seen:
count += 1
seen.append(word)

Replace one item in a string with one item from a list

I have a string and a list:
seq = '01202112'
l = [(0,1,0),(1,1,0)]
I would like a pythonic way of replacing each '2' with the value at the corresponding index in the list l such that I obtain two new strings:
list_seq = [01001110, 01101110]
By using .replace(), I could iterate through l, but I wondered is there a more pythonic way to get list_seq?
I might do something like this:
out = [''.join(c if c != '2' else str(next(f, c)) for c in seq) for f in map(iter, l)]
The basic idea is that we call iter to turn the tuples in l into iterators. At that point every time we call next on them, we get the next element we need to use instead of the '2'.
If this is too compact, the logic might be easier to read as a function:
def replace(seq, to_replace, fill):
fill = iter(fill)
for element in seq:
if element != to_replace:
yield element
else:
yield next(fill, element)
giving
In [32]: list(replace([1,2,3,2,2,3,1,2,4,2], to_replace=2, fill="apple"))
Out[32]: [1, 'a', 3, 'p', 'p', 3, 1, 'l', 4, 'e']
Thanks to #DanD in the comments for noting that I had assumed I'd always have enough characters to fill from! We'll follow his suggestion to keep the original characters if we run out, but modifying this approach to behave differently is straightforward and left as an exercise for the reader. :-)
[''.join([str(next(digit, 0)) if x is '2' else x for x in seq])
for digit in map(iter, l)]
I don't know if this solution is 'more pythonic' but:
def my_replace(s, c=None, *other):
return s if c is None else my_replace(s.replace('2', str(c), 1), *other)
seq = '01202112'
l = [(0,1,0),(1,1,0)]
list_req = [my_replace(seq, *x) for x in l]
seq = '01202112'
li = [(0,1,0),(1,1,0)]
def grunch(s, tu):
it = map(str,tu)
return ''.join(next(it) if c=='2' else c for c in s)
list_seq = [grunch(seq,tu) for tu in li]

How to separate uppercase and lowercase letters in a string?

I have written code that separates the characters at 'even' and 'odd' indices, and I would like to modify it so that it separates characters by upper/lower case.
I can't figure out how to do this for a string such as "AbBZxYp". I have tried using .lower and .upper but I think I'm using them incorrectly.
def upperLower(string):
odds=""
evens=""
for index in range(len(string)):
if index % 2 == 0:
evens = evens + string[index]
if not (index % 2 == 0):
odds = odds + string[index]
print "Odds: ", odds
print "Evens: ", evens
Are you looking to get two strings, one with all the uppercase letters and another with all the lowercase letters? Below is a function that will return two strings, the upper then the lowercase:
def split_upper_lower(input):
upper = ''.join([x for x in input if x.isupper()])
lower = ''.join([x for x in input if x.islower()])
return upper, lower
You can then call it with the following:
upper, lower = split_upper_lower('AbBZxYp')
which gives you two variables, upper and lower. Use them as necessary.
>>> filter(str.isupper, "AbBZxYp")
'ABZY'
>>> filter(str.islower, "AbBZxYp")
'bxp'
Btw, for odd/even index you could just do this:
>>> "AbBZxYp"[::2]
'ABxp'
>>> "AbBZxYp"[1::2]
'bZY'
There is an itertools recipe called partition that can do this. Here is the implementation:
From itertools recipes:
def partition(pred, iterable):
'Use a predicate to partition entries into false entries and true entries'
# partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9
t1, t2 = tee(iterable)
return filterfalse(pred, t1), filter(pred, t2)
Upper and Lowercase Letters
You can manually implement the latter recipe, or install a library that implements it for you, e.g. pip install more_itertools:
import more_itertools as mit
iterable = "AbBZxYp"
pred = lambda x: x.islower()
children = mit.partition(pred, iterable)
[list(c) for c in children]
# [['A', 'B', 'Z', 'Y'], ['b', 'x', 'p']]
Here partition uses a predicate function to determine if each item in an iterable is lowercase. If not, it is filtered into the false group. Otherwise, it is filtered into the group of true items. We iterate to expose these groups.
Even and Odd Indices
You can modify this to work for odd and even indices as well:
import itertools as it
import more_itertools as mit
iterable = "AbBZxYp"
pred = lambda x: x[0] % 2 != 0
children = mit.partition(pred, tuple(zip(it.count(), iterable)))
[[i[1] for i in list(c)] for c in children]
# [['A', 'B', 'x', 'p'], ['b', 'Z', 'Y']]
Here we zip an itertools.count() object to enumerate the iterable. Then we iterate the children so that the sub items yield the letters only.
See also more_itertools docs for more tools.

Not able to remove characters from a string in Python

I am trying to loop through a string x that represents the alphabet and at the same time compare those values with a list that contains some specific letters.
If there is a match in both the list and the string x, it should remove the specific character from the string x. It is a very simple and straightforward piece of code. I've followed the .replace method to the T. However, when I ran the code, the string x still shows up in its original state.
Here is my working code:
lettersGuessed = ['e', 'i', 'k', 'p', 'r', 's']
x = 'abcdefghijklmnopqrstuvwxyz'
for i in range(len(x)):
if x[i] in lettersGuessed:
x.replace(x[i],'')
print x "Available Letters"
Try the following
x = x.replace(x[i], '')
You're not reassigning the changed value back to the original string.
Simple mistake.
x = x.replace(x[i],'')
You can use join and a generator expression:
print("Available Letters","".join(ch if ch not in lettersGuessed else "" for ch in x ))
Using a loop, just iterate over the characters in lettersGuessed and update x each time:
for ch in lettersGuessed:
x = x.replace(ch,'') # assign the updated string to x
print("Available Letters",x)
Or iterating over x is the same logic:
for ch in x:
if ch in lettersGuessed:
x = x.replace(ch,'')
strings are immutable so you cannot change the string in place. You need to reassign x to the new string created with x.replace(ch,'')
In [1]: x = 'abcdefghijklmnopqrstuvwxyz'
In [2]: id(x)
Out[2]: 139933694754864
In [3]: id(x.replace("a","foo")) # creates a new object
Out[3]: 139933684264296
In [7]: x
Out[7]: 'abcdefghijklmnopqrstuvwxyz' # no change
In [8]: id(x)
Out[8]: 139933694754864 # still the same object
You could use python sets to achieve this :
a = ['a','b','d']
b = "abcdefgh"
print ''.join(sorted(list(set(b) - set(a))))
output:
cefgh Available letters
Or use list comprehensions to achieve this :
a = ['a','b','d']
b = "abcdefgh"
print ''.join([x for x in b if x not in a])

List Comprehension for removing duplicates of characters in a string [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do you remove duplicates from a list whilst preserving order?
So the idea is the program takes a string of characters and removes the same
string with any duplicated character only appearing
once -- removing any duplicated copy of a character.
So Iowa stays Iowa but the word eventually would become eventually
Here is an inefficient method:
x = 'eventually'
newx = ''.join([c for i,c in enumerate(x) if c not in x[:i]])
I don't think that there is an efficient way to do it in a list comprehension.
Here it is as an O(n) (average case) generator expression. The others are all roughly O(n2).
chars = set()
string = "aaaaa"
newstring = ''.join(chars.add(char) or char for char in string if char not in chars)
It works because set.add returns None, so the or will always cause the character to be yielded from the generator expression when the character isn't already in the set.
Edit: Also see refaim's solutions. My solution is like his second one, but it uses the set in the opposite way.
My take on his OrderedDict solution:
''.join(OrderedDict((char, None) for char in word))
Without list comprehensions:
from collections import OrderedDict
word = 'eventually'
print ''.join(OrderedDict(zip(word, range(len(word)))).keys())
With list comprehensions (quick and dirty solution):
word = 'eventually'
uniq = set(word)
print ''.join(c for c in word if c in uniq and not uniq.discard(c))
>>> s='eventually'
>>> "".join([c for i,c in enumerate(s) if i==s.find(c)])
'evntualy'
note that using a list comprehension with join() is silly when you can just use a generator expression. You should tell your teacher to update their question
You could make a set from the string, then join it together again. This works since sets can only contain unique values. The order wont be the same though:
In [1]: myString = "mississippi"
In [2]: set(myString))
Out[2]: set(['i', 'm', 'p', 's'])
In [3]: print "".join(set(myString))
Out[3]: ipsm
In [4]: set("iowa")
Out[4]: set(['a', 'i', 'o', 'w'])
In [5]: set("eventually")
Out[5]: set(['a', 'e', 'l', 'n', 't', 'u', 'v', 'y'])
Edit: Just saw the "List Comprehension" in the title so this probably isnt what your looking for.
Create a set from the original string, and then sort by position of character in original string:
>>> s='eventually'
>>> ''.join(sorted(set(s), key=s.index))
'evntualy'
Taken from this question, I think this is the fastest way:
>>> def remove_dupes(str):
... chars = set()
... chars_add = chars.add
... return ''.join(c for c in str if c not in chars and not chars_add(c))
...
>>> remove_dupes('hello')
'helo'
>>> remove_dupes('testing')
'tesing'
word = "eventually"
evntualy = ''.join(
c
for d in [dict(zip(word, word))]
for c in word
if d.pop(c, None) is not None)
Riffing off of agf's (clever) solution but without making a set outside of the generator expression:
evntualy = ''.join(s.add(c) or c for s in [set()] for c in word if c not in s)

Categories

Resources