Code:
'''
Program to Compress a string using Dictonaries.
Input samle--> 'AAAABBBBCCCCDDDDaa'
output sample--> 'A4B4C4D4a2'
'''
# Function declaration
def string_compression(str):
d = {}
x = []
# Generating Key-Value pair for entire string
for i in str:
if i not in d.keys():
d[i] = 1
else:
d[i] = d[i] + 1
# Copying Key Value Pairs in a list
for key,value in d.items():
x.append(key)
x.append(value)
# Printing a Cocktail list of Strings and Integers
print(x)
# Converting Integers in list x to Strings and Joining them
for i in x[1::2]:
x[i] = str(x[i])
print(''.join(x))
#print(''.join(map(str, x)))
y = 'AAAABBBBCCCCDDDDaa'
string_compression(y) # Function Call
Output:
['A', 4, 'B', 4, 'C', 4, 'D', 4, 'a', 2]
Traceback (most recent call last):
File "string_compression.py", line 36, in <module>
string_compression(y) # Function Call
File "string_compression.py", line 30, in string_compression
x[i] = str(x[i])
TypeError: 'str' object is not callable
I'm able to print x as list,
But I'm unable to print the list in string format.
Also I tried all the possible combinations of solutions as per my previous post:
Converting list of Integers and Strings to purely a string
But The link's solutions are working only if I try to run the code in a new file taking only 2 lines of code as:
x = ['A', 4, 'B', 4, 'C', 4, 'D', 4, 'a', 2]
print(''.join(map(str, x)))
Why are any of the methods not working here in this above code? Any concept which I'm lagging?
Change the param from built-in keyword type str to something else, perhaps s, also switching to map in joining:
def string_compression(s):
d = {}
x = []
# Generating Key-Value pair for entire string
for i in s:
if i not in d.keys():
d[i] = 1
else:
d[i] = d[i] + 1
# Copying Key Value Pairs in a list
for key,value in d.items():
x.append(key)
x.append(value)
# Printing a Cocktail list of Strings and Integers
print(x)
# Converting Integers in list x to Strings and Joining them
for i in x[1::2]:
x[i] = str(x[i])
print(''.join(map(str, x)))
y = 'AAAABBBBCCCCDDDDaa'
string_compression(y) # Function Call
OUTPUT:
['A', 4, 'B', 4, 'C', 4, 'D', 4, 'a', 2]
A4B4C4D4a2
Your particular problem is you're using the name str for the argument of your function, which shadows the builtin str() function you're trying to call.
However, there are also some bugs in your algorithm – dictionaries aren't ordered (not before Python 3.6 anyway) so you aren't guaranteed that you get the same order of characters back. Also, if the input string is aaaaabbbbbaaaaa, you'll end up with something like a10b5, which isn't reversible to aaaaaabbbbbaaaaa. (I'm not going to go into how strings with numbers can't be "compressed" reversibly with this, but that's also an issue.)
I'd do something like this:
def string_rle(s):
parts = []
for c in s:
if not parts or c != parts[-1][0]:
parts.append([c, 1])
else:
parts[-1][-1] += 1
return ''.join('%s%d' % (part[0], part[1]) for part in parts)
print(string_rle('AAAABBBBCCCCDDDDaa'))
The idea is that parts holds an ordered list of pairs of a character and a count, and for each character in the original string, we see if the current pair (parts[-1]) has the same character; if it does, we increment the counter, otherwise, we generate a new pair.
You have overritten the 'str' function in the function definition as a function argument:
def string_compression(str): # your argument overrides the 'str' function
so then when you are trying to call the 'str' function here:
x[i] = str(x[i])
you are actually calling the 'string_compression' argument, which is not callable.
Change the variable 'str' name to something else.
BTW, this for loop:
for i in x[1::2]:
x[i] = str(x[i])
produces a list [C, a, C, C, C] which is not what you are looking for. You need to get the integers indexes not the integers itself.
for i in range(1,len(x),2):
print str(x[i])
Related
I am trying to take a list with a mix of integers and strings, filter out the strings in the list, and only keep the integers. An example of a list I might have to filter:
filter_list([1,2,'a','b'])
Here is my code:
new_list = list()
def filter_list(l):
for step in l:
if type(l[step]) == int:
new_list.append(int(l[step]))
else:
pass
return new_list
However, I am getting the error:
Traceback (most recent call last):
File "tests.py", line 3, in <module>
test.assert_equals(filter_list([1,2,'a','b']),[1,2])
File "/workspace/default/solution.py", line 4, in filter_list
if type(l[step]) is int:
TypeError: list indices must be integers or slices, not str
What am I doing wrong? The traceback is coming from running the test file which tests to see if my code works, but the actual error is from my code.
Other people already provided solutions, but they didn't explain, why your didn't work.
Your list is [1,2,'a','b']. Python throws exception at element 'a'. If you run this code:
list = [1,2,'a','b']
for step in list:
print(step)
Your output would be:
1
2
a
b
As you can see 3rd element of your list is string - that's why you get
TypeError: list indices must be integers or slices, not str
To get index of element use enumerate:
list = [1,2,'a','b']
for index, value in enumarate(list):
print(f"{index}: {value}")
Output:
0: 1
1: 2
2: a
3: b
Your code with enumarate:
new_list = list()
def filter_list(l):
for step, value in enumarate(l):
if type(l[step]) == int:
new_list.append(int(l[step]))
else:
pass
return new_list
A for loop in python iterates over the elements, not the indices, of an iterable object. That means that step takes on the values 1, 2, 'a', 'b', not 0, 1, 2, 3. In other languages, this sort of iteration is sometimes called foreach.
While the first two list elements are valid indices, the last two are not. Also, l[2] is 'a', which is not an integer. The most basic fix is to fix your indexing. The example below also initializes the new list inside the function, since you probably don't want to keep appending to the same global list (run the function twice to see what I mean).
def filter_list(l):
new_list = []
for item in l:
if type(item) == int:
new_list.append(item)
return new_list
You can also make your check of integerness more idiomatic. If you only want to keep objects that are already integers, use isinstance(item, int) rather than type(item) == int. If you want to allow things like the string '123' to pass through, use int(item) in a try-except instead:
def filter_list(l):
new_list = []
for item in l:
try:
new_list.append(int(item))
except TypeError, ValueError:
pass
return new_list
The most idiomatic way to express the version with a conditional is to use a list comprehension:
def filter_list(l):
return [item for item in l if isinstance(item, int)]
Try this instead:
list(filter(lambda x: isinstance(x, int), [1,2,'a','b']))
Python's built in filter function has two arguments, first one is a function that you want to apply the filter with and the second one is the object being filtered.
output:
[1, 2]
This question already has answers here:
Sorting python list to make letters come before numbers
(6 answers)
Closed 1 year ago.
I am creating a program that sorts the user's input in ascending and descending order. What I have tried below was only working with integers, I tried to use str but only the letters are sorted.
a = input("Enter your list with space:")
b = list(map(int, a.split()))
print("Original List:", b)
print("Sorted Ascending Order:")
print (sorted(b))
print("Sorted Descending Order:")
print(sorted(b, reverse=True))
If I would enter 10 2 3, the result would be [2, 3, 10] and [10, 3, 2].
What I want is to sort something like 10 a 2 c that would result to [a, c, 2, 10] and [10, 2, c, a].
I would like anyone to guide me on what should I use/add or change.
Actually, what I want to ask is on how could I sort a list with numbers and letters. I have tried some code before but it would read 10 as 1 and 0. Some of it would only sort the letters and not the numbers.
You can write a custom comparator that first sorts by isinstance to prioritize int over str, then defer to the value itself.
>>> b = [10, 'a', 2, 'c']
>>> sorted(b, key=lambda i: (isinstance(i, int), i), reverse=True)
[10, 2, 'c', 'a']
>>> sorted(b, key=lambda i: (isinstance(i, int), i))
['a', 'c', 2, 10]
First, you need to convert only those values to integers that are actually valid integers.
user_in = input("Enter your list with spaces: ")
user_list = user_in.split()
for i, u in enumerate(user_list):
try:
# Try to replace the string with the integer
user_list[i] = int(u)
except ValueError:
# ValueError happened. Invalid integer. Must be a letter
pass
With this, you now have a list that contains strings and integers.
print(user_list)
# Output:
[10, 'a', 2, 'c']
Next, you need to tell Python how to compare a letter with a number, because Python has no way of doing that out of the box. However, we can use the key argument of the sorted() function to tell Python to pass each item to a function, and use its return value as the value to sort by. Finally, we can leverage the fact that Python can sort tuples, and that tuples are sorted by their first element, then by their second element, and so on. If we return a tuple containing a boolean (is this item an integer?) and the item itself, then we can ensure all strings are sorted before integers in ascending order.
def keyfunc(item):
is_int = isinstance(item, int)
return (is_int, item)
sorted_list = sorted(user_list, key=keyfunc)
print(sorted_list)
# Output:
['a', 'c', 2, 10]
Of course, you could condense all this down to a lambda function like Cory's answer, but I have included the verbose approach for clarity.
Hear me out, I do not simply want someone to solve this problem for me. I know it is not 100% complete yet, but currently when I run the program I get an error about "Can't convert 'list' object to str implicitly" I'm looking for help on how to fix this and why it is does this.
Here is the problem
Write code to print out each thing in the list of lists, L, with a '*' after it like
1*2*3*4*...8*a*b*c*d*
This requires knowing the print statement and using the end or sep argument option
Here is my list, sorry for not putting it in earlier
L = [[1,2,3,4],[5,6,7,8],['a','b','c','d']]
Here is my code at the moment
def ball(x): #random function name with one parameter
q = '' #
i = 0
if type(x) != list: #verifies input is a list
return"Error"
for i in x: #Looks at each variable in list
for j in i: #Goes into second layer of lists
q = q + j + '*'
print(q)
The reason for your error
"Can't convert 'list' object to str implicitly"
is that you're using the wrong variable in your nested for loops. Where you're concatenating values to your q variable, you mistakenly put q = q + i when you wanted q = q + j. You also will want to cast the value of j as a string so it can be concatenated with q. In order to get your desired output, you can simply add an asterisk into that statement - something like the following: q = q + str(j) + '*'. On a completely unrelated note, your else statement that just has "Mistake" in it should be removed completely - it doesn't follow an if and it doesn't actually return or assign to a variable.
Note that this is not the most elegant way to go about solving this problem. I agree with ilent2 that you should take a look at both list comprehension and the str.join() method.
If you have a list of strings,
myList = ['a', '123', 'another', 'and another']
You can join them using the str.join function:
Help on method_descriptor:
join(...)
S.join(iterable) -> string
Return a string which is the concatenation of the strings in the
iterable. The separator between elements is S.
myString = '#'.join(myList)
If your list contains mixed types or non-strings you need to convert each item to a string first:
anotherList = [1, 2, 'asdf', 'bwsg']
anotherString = '*'.join([str(s) for s in anotherList])
You might want to read about list comprehension or more about the join function. Note, the above doesn't print the output (unless you are using the interactive console), if you want the output to be printed you will need call print too
print myString
print anotherString
And, if you are working with lists-of-lists you may need to change how you convert each sub-list into a string (depending on your desired output):
myListList = [[1, 2, 3, 4], [2, 3, 6, 5], [6, 4, 3, 1]]
myOtherString = '#'.join(['*'.join([str(s) for s in a]) for a in myListList])
The last line is a little complicated to read, you might want to rewrite it as a nested for loop instead.
I have a line of code which is:
D = {'h' : 'hh' , 'e' : 'ee'}
str = 'hello'
data = ''.join(map(lambda x:D.get(x,x),str))
print data
this gives an output -> hheello
I am trying to understand how does map function work here. Does map take each character of
the string and compare it with dictionary key, and give back the corresponding key value?
How does it do for each character here? There is no iteration. Is there any good example to understand this better?
There is no loop because map requires an "iterable" (i.e. an object on which you can do an iteration) and does the loop itself.
map, if not present natively, could be implemented as:
def map(f, it):
return [f(x) for x in it]
or, even more explicitly, as:
def map(f, it):
result = []
for x in it:
result.append(f(x))
return result
In Python a string is an iterable, and on iteration loops over the characters in the string. For example
map(ord, "hello")
returns
[104, 101, 108, 108, 111]
because those are the character codes for the chars in the string.
Map just applies the function to each item of a list. E.g.,
map(lambda x: 10*x, [1,2,3,4])
gives
[10, 20, 30, 40]
It takes individual elements of str. Following is the readable code for the same implementation:
D = { 'h' : 'hh' , 'e' : 'ee'}
str = 'hello'
returns = [] # create list for storing return value from function
def myLambda(x): # function does lambda
return D.get(x,x)
for x in str: #map==> pass iterable
returns.append(myLambda(x)) #for each element get equivalent string from dictionary and append to list
print ''.join(returns) #join for showing result
Generally speaking, a map operation works like this:
MAP (f,L) returns L'
Input:
L is a list of n elements [ e1 , e2 , ... , en ]
f is a function
Output
L' is the list L after the application of f to each element individually: [ f(e1) , f(e2) , ... , f(en) ]
So, in your case, the join operation, which operates on lists, starts with the empty string and repeatedly concatenates each element e obtained in the following way:
Take a character x from str; return D.get(x,x)
Note that the above (which is the explaining of the map operation) will give you 'hh' and 'ee' with input 'h' and input 'e' respectively, while it will leave the other characters as they are.
Since str is a string, map() will apply the function (lambda in this case) to every item of the string. [map()][2] works on iterables and sequences, so it can work in a string because a string is a sequence.
Try this so you get the idea:
str2 = "123"
print map(int, str2)
>>> [1, 2, 3]
In this case you are casting each letter in str2 to int:
int("1") -> 1
int("2") -> 2
int("3") -> 3
and return them in a list:
[1, 2, 3]
Note: Don't use Python built-in names as names of variables. Don't use str as the name of a variable because you are hiding its built-in implementation. Use str1, my_str o, s ... instead.
I have a Python list mylist whose elements are a sublist containing a string of a letter and number. I was wondering how I could split mylist by the character at the start of the string without using code with individual statements/cases for each character.
Say I want to split mylist into lists a, b, c:
mylist = [['a1'],['a2'],['c1'],['b1']]
a = [['a1'],['a2']]
b = [['b1']]
c = [['c1']]
It is important that I keep them as a list-of-lists (even though it's only a single element in each sublist).
This will work:
import itertools as it
mylist = [['a1'],['a2'],['c1'],['b1']]
keyfunc = lambda x: x[0][0]
mylist = sorted(mylist, key=keyfunc)
a, b, c = [list(g) for k, g in it.groupby(mylist, keyfunc)]
The line where sorted() is used is necessary only if the elements in mylist are not already sorted by the character at the start of the string.
EDIT :
As pointed out in the comments, a more general solution (one that does not restrict the number of variables to just three) would be using dictionary comprehensions (available in Python 2.7+) like this:
result_dict = {k: list(g) for k, g in it.groupby(mylist, keyfunc)}
Now the answer is keyed in the dictionary by the first character:
result_dict['a']
> [['a1'],['a2']]
result_dict['b']
> [['b1']]
result_dict['c']
> [['c1']]
Using a dictionary could work too
mylist = [['a1'],['a2'],['c1'],['b1']]
from collections import defaultdict
dicto = defaultdict(list)
for ele in mylist:
dicto[ele[0][0]].append(ele)
Result:
>>> dicto
defaultdict(<type 'list'>, {'a': [['a1'], ['a2']], 'c': [['c1']], 'b': [['b1']]})
It does not give the exact result you were asking for; however, it is quite easy to access a list of lists associated with each letter
>>> dicto['a']
[['a1'], ['a2']]
You can also get these sublists by using a simple function:
def get_items(mylist, letter):
return [item for item in mylist if item[0][0] == letter]
The expression item[0][0] simply means to get the first letter of the first element of the current item. You can then call the function for each letter:
a = get_items(mylist, 'a')
b = get_items(mylist, 'b')
c = get_items(mylist, 'c')