The first function is able to separate each letter of a string and list how many times that letter appears. For example:
print(rlencode("Hello!"))
[('H', 1), ('e', 1), ('l', 2), ('o', 1), ('!', 1)]
How do I get rldecode(rle): do the the complete opposite of rlencode(s) so that rldecode(rlencode(x)) == x returns True
def rlencode(s):
"""
signature: str -> list(tuple(str, int))
"""
string=[]
count=1
for i in range(1,len(s)):
if s[i] == s[i-1]:
count += 1
else:
string.append((s[i-1], count))
count=1
if i == len(s)-1:
string.append((s[i], count))
return string
def rldecode(rle):
"""
#signature: list(tuple(str, int)) -> str
#"""
string=" "
count=1
for i in rle:
if i == rle:
string += i
return string
You can use the fact that you can multiply a string by a number to repeat it and use `''.join() to bring the elements of the list together.
To show the effect of string multiplication, I multiplied "a" by 5
"a"*5 #'aaaaa'
Using that in a comprehension will give you
str = [char[0]*char[1] for char in rle] #['H', 'e', 'll', 'o', '!']
Then add in the ''.join() and you have your answer.
l = [('H', 1), ('e', 1), ('l', 2), ('o', 1), ('!', 1)]
str = ''.join(char[0]*char[1] for char in rle) #'Hello!'
So your function would be
def rldecode(rle):
"""
signature: list(tuple(str, int)) -> str
"""
return ''.join(char[0]*char[1] for char in rle)
Also, if you would like to make your rlencode a little cleaner, you can simplify it a little bit by using enumerate to help you keep your position in the string and check if you're about to hit either a new character or the end of the string. You just have to increment the counter on each loop.
def rlencode(s):
output = []
count = 0
for i, char in enumerate(s):
count += 1
if (i == (len(s)-1)) or (char != s[i+1]):
output.append((char, count))
count = 0
return output
Use join:
b = [('H', 1), ('e', 1), ('l', 2), ('o', 1), ('!', 1)]
''.join([c[0] * c[1] for c in b])
Hello!
You can also use list comprehensions for your initial function.
You can use collections.Counter.elements():
from collections import Counter
l = [('H', 1), ('e', 1), ('l', 2), ('o', 1), ('!', 1)]
print(''.join(Counter(dict(l)).elements()))
This outputs:
Hello!
A simple, readable solution is to iterate over all of the tuples in the list returned by rlencode and construct a new string from each letter (and it's frequency) like so:
def rldecode(rle):
string = ''
for letter, n in rle:
string += letter*n
return string
An answer that's easy to read but also accounts for ordering in the problem:
def rlencode(s):
"""
signature: str -> list(tuple(str, int, list(int)))
"""
result=[]
frequency=1
for i in range(len(s)):
letters = [item[0] for item in result]
if s[i] in letters:
idx = letters.index(s[i])
frequency=result[idx][1]
frequency+=1
positions= result[idx][2]
positions.append(i)
result[idx] = (s[i],count,lst)
else:
result.append((s[i],1,[i]))
return result
def rldecode(rle):
"""
#signature: list(tuple(str, int, list(int))) -> str
#"""
frequencies = [i[1] for i in rle]
total_length = sum(frequencies)
char_list=[None]*total_length
for c in rle:
for pos in c[2]:
char_list[pos] = c[0]
return "".join(char_list)
text = "This is a lot of text where ordering matters"
encoded = rlencode(text)
print(encoded)
decoded = rldecode(encoded)
print(decoded)
I adapted it from the answer posted by #Brian Cohan
It should be noted that the answer is computationally expensive because of .index() if letter grows really long as explained in this SO post
Related
dna_string = 'ATGCTTCAGAAAGGTCTTACG'
length = len(dna_string)
print("There are %d letters in this DNA string." % length)
print('Now here are the amounts for the letters "A", "C", "T", "G" in order.\n')
combien_a = dna_string.count('A')
combien_c = dna_string.count('C')
combien_t = dna_string.count('T')
combien_g = dna_string.count('G')
print(str(combien_a) + ' ' + str(combien_c) + ' ' + str(combien_g) + ' ' + str(combien_t))
you can try this.
dna_string = 'ATGCTTCAGAAAGGTCTTACG'
print(*[dna_string.count(a) for a in ['A','C','T','G']],sep=" ")
You can add these to a list like:
combien = [dna_string.count(x) for x in ['A','C','T','G']]
collections.Counter is useful for counting numbers.
from collections import Counter
dna_string = 'ATGCTTCAGAAAGGTCTTACG'
# Counter object works like a dictionary with element as key and count as value
combien = Counter(dna_string)
print(f"There are {len(dna_string)} letters in this DNA string.")
# you can convert the Counter to a list of (elem, count) tuples
print(list(combien.items())
Output:
There are 21 letters in this DNA string.
[('A', 6), ('T', 6), ('G', 5), ('C', 4)]
Elements are ordered from higher count to lower count in Counter, if you want another order for the result you may sort like this:
print(sorted(list(combien.items()), key=lambda x: ["A", "C", "T", "G"].index(x[0])))
Output:
[('A', 6), ('C', 4), ('T', 6), ('G', 5)]
I want to write screen Capital letters and index numbers in word="WElCMMerC".For example [(0,W),(1,E),(3,C),(4,M),(5,M)...]
def cap(word):
w=list(enumerate(i) for i in word if i!=i.lower())
print (w)
print(cap("WElCMMerC"))
You can loop over the result of enumerate, and keep only those which have an uppercase letter (using isupper to check for that), and return the list w, don't print inside the function:
def cap(word):
w = [i for i in enumerate(word) if i[1].isupper()]
return w
print(cap("WElCMMerC"))
Output:
[(0, 'W'), (1, 'E'), (3, 'C'), (4, 'M'), (5, 'M'), (8, 'C')]
You made a list of enumerate objects. Read the documentation: enumerate is an iterator, much like range. Rather, you need to use the enumeration.
return [(idx, letter)
for idx, letter in enumerate(word)
if letter.isupper()]
In English:
Return the pair of index and letter
for each index, letter pair in the word
but only when the letter is upper-case.
I'm trying to create a program without importing anything. The program lets the user input a passage, then prints how many A's there are in the message, how many B's, etc.
So it works...it's just VERY long. I'm new to coding, and I know that there is a way to simplify the code below with def but I'm not really sure how. Can anyone help?
You need no methods, but you can definately cut it short:
String can be used as an array of characters.
You can use the index method to determine what is the position of the letter in the alphabet.
You can iterate a zipped list of pairs from the alphabet and the counter list, to produce the output.
Use if letter in alphabet as a guard to ensure the letter is valid for the alphabet, instead of hard coding the alphabet. That way you can even expand your alphabet. (Note that the counter is set to the length of the alphabet).
Here is a suggestion:
message = input('what is your message? ').upper()
alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
counter = [0] * len(alphabet)
for letter in message:
if letter in alphabet:
counter[alphabet.index(letter)] += 1
for letter, count in zip(alphabet, counter):
print(letter, ':', count)
One can do it with a one line instruction, where we make use of:
count method of string that returns the numbers of element contained in a string
chr function that gives a character from an int. chr(65) gives a A, chr(66) gives a B, ...
join function that concatenates strings of a list
The result looks like
message = input('what is your message? ').upper()
print('\n'.join([chr(65+i)+':'+str(message.count(chr(65+i))) for i in range(26)]))
For a very short and elegant solution use the Counter unit from the collections module:
from collections import Counter
message=raw_input("what is your message?")
message=message.upper()
c = Counter(message)
print c.most_common()
This counts every kind of letter in the message. And it can even sort the result for you quickly. Here is a sample dialog:
"what is your message?Hi there, new Pythonist!
[(' ', 3), ('E', 3), ('H', 3), ('T', 3), ('I', 2), ('N', 2), ('!', 1), (',', 1), ('O', 1), ('P', 1), ('S', 1), ('R', 1), ('W', 1), ('Y', 1)]"
I have a little question about how to check and compare two or more characters in the list in Python.
For example, I have a string "cdcdccddd". I made a list from this string to easier comparing the characters. And the needed output is:
c: 1 d: 1 c: 1 d: 1 c: 2 d: 3
So it is counting the characters, if first is not the same as the second, the counter = 1, if the second is the same as third, then counter is +1 and need check the third with fourth and so on.
I got so far this algorithm:
text = "cdcdccddd"
l = []
l = list(text)
print list(text)
for n in range(0,len(l)):
le = len(l[n])
if l[n] == l[n+1]:
le += 1
if l[n+1] == l[n+2]:
le += 1
print l[n], ':' , le
else:
print l[n], ':', le
but its not working good, because its counts the first and second element, but not the second and third. For this output will be:
c : 1
d : 1
c : 1
d : 1
c : 2
c : 1
d : 3
How to make this algorithm better?
Thank you!
You can use itertools.groupby:
from itertools import groupby
s = "cdcdccddd"
print([(k, sum(1 for _ in v)) for k,v in groupby(s)])
[('c', 1), ('d', 1), ('c', 1), ('d', 1), ('c', 2), ('d', 3)]
Consecutive chars will be grouped together, so each k is the char of that group, calling sum(1 for _ in v) gives us the length of each group so we end up with (char, len(group)) pairs.
If we run it in ipython and call list on each v it should be really clear what is happening:
In [3]: from itertools import groupby
In [4]: s = "cdcdccddd"
In [5]: [(k, list(v)) for k,v in groupby(s)]
Out[5]:
[('c', ['c']),
('d', ['d']),
('c', ['c']),
('d', ['d']),
('c', ['c', 'c']),
('d', ['d', 'd', 'd'])]
We can also roll our own pretty easily:
def my_groupby(s):
# create an iterator
it = iter(s)
# set consec_count, to one and pull first char from s
consec_count, prev = 1, next(it)
# iterate over the rest of the string
for ele in it:
# if last and current char are different
# yield previous char, consec_count and reset
if prev != ele:
yield prev,
consec_count, = 0
prev = ele
consec_count, += 1
yield ele, consec_count
Which gives us the same:
In [8]: list(my_groupby(s))
Out[8]: [('c', 1), ('d', 1), ('c', 1), ('d', 1), ('c', 2), ('d', 3)]
That looks like a regular expression of repeating characters, so you can use a regex with repeated characters and then find the length of each match:
import re
text = "cdcdccddd"
matches = re.findall(r'(.)(\1*)', text)
result = ['{}: {}'.format(match[0], len(''.join(match))) for match in matches]
Result:
>>> print(*result, sep='\n')
c: 1
d: 1
c: 1
d: 1
c: 2
d: 3
First thing, strings are already lists in python, so you can just say for character in text: to get each of the characters out.
I would try something like this:
currentchar = text[0]
currentcount = 0
for c in text[1:]:
if c == currentchar:
currentcount += 1
else:
print(currentchar + ": " + str(currentcount+1))
currentchar = c
currentcount = 0
print(currentchar + ": " + str(currentcount+1))
(If you have a better title, do edit, I couldn't explain it properly! :)
So this is my code:
with open('cipher.txt') as f:
f = f.read().replace(' ', '')
new = []
let = []
for i in f:
let.append(i)
if i.count(i) > 1:
i.count(i) == 1
else:
new = sorted([i + ' ' + str(f.count(i)) for i in f])
for o in new:
print(o)
And this is cipher.txt:
xli uymgo fvsar jsb
I'm supposed to print out the letters used and how many times they are used, my code works, but I need it alphabetical, I tried putting them in a list list(a) and then sorting them, but i didn't quite get it, any ideas? Thanks in advance!
Whenever dealing with counting, you can use collections.Counter here:
>>> from collections import Counter
>>> print sorted(Counter('xli uymgo fvsar jsb'.replace(' ', '')).most_common())
[('a', 1), ('b', 1), ('f', 1), ('g', 1), ('i', 1), ('j', 1), ('l', 1), ('m', 1), ('o', 1), ('r', 1), ('s', 2), ('u', 1), ('v', 1), ('x', 1), ('y', 1)]
If you can't import any modules, then you can append a to a list and then sort it:
new = []
for i in f:
new.append(i + ' ' + str(f.count(i)) # Note that i is a string, so str() is unnecessary
Or, using a list comprehension:
new = [i + ' ' + str(f.count(i)) for i in f]
Finally, to sort it, just put sorted() around it. No extra parameters are needed because your outcome is alphabetical :).
Here's a oneliner without imports:
{s[i]: n for i, n in enumerate(map(s.count, s))}
And in alphabetical order (if the above is d):
for k in sorted(d): print k, d[k]
Or another version (oneliner alphabetical):
sorted(set([(s[i], n) for i, n in enumerate(map(s.count, s))]))