I am trying to understand a open source code from the following link:
https://github.com/alexbaucom17/DominoRobot/blob/master/src/tools/plot_logs.py
Can anybody explain what exactly happening in the line: if c in ['[',']',',','\n']:
def get_value(line, path):
idx = 0
for p in path:
idx = line.find(p, idx)
idx += len(p)
data = ""
for c in line[idx:]:
if c in ['[',']',',','\n']:
break
elif c in [':',' ']:
continue
else:
data += c
return float(data)
The in operator checks for containment. So it is literally checking if the length-1 string c is one of the elements [],\n. If so, it ends the loop using break if not, it checks against : . Because of how containment checks work, and the fact that c is guaranteed by the loop to be a single character, you can implement the check like this:
if c in '[],\n':
break
elif c in ': ':
continue
else:
data += c
While we're here, data += c is not a great way to accumulate strings, since they are immutable. You may be better off appending to a list instead:
data = []
for c in line[idx:]:
...
data = ''.join(data)
Better yet, record the index of the first element you find, slice the original, and remove the two undesirable characters:
for i, c in line[idx:]:
if c in '[],\n':
break
data = line[idx:i].replace(':', '').replace(' ', '')
This code is not written to be read easily.
It would be better if it included some spaces to make it more clear:
if c in ['[', ']', ',', '\n']:
['[', ']', ',', '\n'] is a list of four single-character string literals:
'['
']'
','
'\n' (the line break character)
This line tests if c is contained in the list, i.e., if it is equal to any of those four characters.
What can be even more confusing is that [, ], and , appear both as one of the four characters to be tested, and as part of the list syntax [x, y, z].
It is set to true if c has one of the values in the list, so if it is one of those characters.
It is the same as checking if c is equal to each item from the list, and if it is equal to any of them, it returns true.
Related
The question for the code is 'input a word and check whether the first character of the word is repeated in the word again or not. If yes then change all the repeating characters to $ except the first character.'
So I coded the following and used the logic to start the loop from the second character of the word so that first character remains unchanged.
a=input()
for i in range(1,len(a)):
if(a[i]==a[0]):
b=a.replace(a[i],'$')
print(b)
for the above program I gave the input as 'agra' and to my surprise got the output as '$gr$'. the first character was also changed.
What is the problem with my logic? and what other solution do you suggest?
That is more simply done like:
Code:
b = a[0] + a[1:].replace(a[0], '$')
Test Code:
a = 'stops'
b = a[0] + a[1:].replace(a[0], '$')
print(b)
Results:
stop$
For the correct solution in python, see Stephen Rauch's answer.
I think that what you where trying to achieve, in a very "unpythonic" way, is:
a = input()
b = a # b is a copy
for i in range(1, len(a)):
if a[i] == a[0]:
# replace i-th char of b by '$''
print (b)
How to do that replacement? In python: strings are immutable, that means you cannot replace a char "in place". Try to do this:
a='agra'
a[3] = '$'
And you'll get an error:
TypeError: 'str' object does not support item assignment
If you want to replace the i-th char of a string by $, you have to write:
b = b[:i] + '$' + b[i+1:]
That is: build a new string from b[0], ..., b[i-1], add a $ and continue with b[i+1], ..., b[len(a)-1]. If you use this in your code, you get:
a = input()
b = a
for i in range(1, len(a)):
if a[i] == a[0]:
b = b[:i] + '$' + b[i+1:]
print (b)
Okay, it works but don't do that because it's very "unpythonic" and inefficient.
BEGIN EDIT
By the way, you don't need to replace, you can just build the string character by character:
a = input()
b = a[0] # start with first char
for i in range(1, len(a)):
if a[i] == a[0]:
b += '$' # $ if equals to first char
else:
b += a[i] # else the current char
print (b)
END EDIT
That gave me this idea:
a=input()
b="".join('$' if i!=0 and c==a[0] else c for i,c in enumerate(a))
print(b)
Explanation: the list comprehension takes all characters of a along with their position i (that's what enumerate does). For every couple position, character, if the position is not 0 (not the first character) and if the character is equal to a[0], then put a $. Else put the character itself. Glue everything together to make a new string.
Again, that's not the right way to do what you are trying to do, because there is another way that is neater and easier (see Stephen Rauch's answer), but is shows how you can sometimes handle difficulties in python.
a=input()
for i in range(1,len(a)):
if(a[i]==a[0]):
b=a[1:len(a)].replace(a[i],'$')
print(a[0]+b)
you changed whole word/sentence 'a': a.replace(...)
def double_char(str):
result = ''
for char in str:
result += char + char
return result
this is the function, and when i call it, say:
double_char(dog)
it returns: 'ddoogg'
but on line 4 it's saying result = result + char + char wouldn't that yield ''dogdog? I'm really confused and I think I just dont understand the basics of for loops
You are looping over each character in the string. Imagine it was just:
for char in str:
print char
you go 'd', 'o', 'g' resulting in "dog". Now do the same but 2 times:
for char in str:
print char
print char
you are going 'd', 'd', 'o', 'o' ... resulting in "ddoogg". You are doing the same but constructing the string, then printing it.
You could change your method like this to get what you want.
def double_char(str):
result = str + str
return result
for char in str: loops through every character in str. First time is 'd', second is 'o', third is 'g'. Each of these gets doubled and added to the result. In other words, the first value is not just 'dog' as a whole.
result then looks like this after every iteration
''
'dd'
'ddoo'
'ddoogg'
Let's try running the function:
def double_char(string):
result = ''
for char in string:
result += char + char
return result
print(double_char('dog'))
It returns:
ddoogg
So, what this function is doing is defining an emptry string (result). Many programs do this, they have a temporary empty string. Then that string gets filled up with new information generated by a loop (for char in string:). This loop loops through every character in the string, so d, o, and g. However, there is a twist. This code adds two of the same character for every character inside of the string so, it goes result += 'd' + 'd' on the first iteration. And then result += 'o' + 'o'. The += operater is simply squishing two strings together, like dd from the first iteration with oo from the second iteration. From there, we get ddoo. And it continues until the end of the string. Finally, we use the return statement to send out the new temporary string we created, result.
In python when you run for loop on element like string,
You actually iterate over its characters.
That's why in your example you get each characters twice.
For 'dogdog' use: result = 2*'dog'
As you have mentioned, you are passing string 'dog', implies str:='dog'
Initialised,
result=''
This will initialise result to empty string once.
for char in str:
This loop will iterate through every char.
result += char + char
when you iterate through the loop. char will represent 'd','o','g' respectively at each iteration.
iteration 1:
result=''
char + char := 'd'+'d'
exiting from iteration : result ='dd'
iteration 2:
result='dd'
char + char := 'o'+'o'
exiting from iteration : result ='dd'
iteration 3:
result='ddoo'
char + char := 'g'+'g'
exiting from iteration : result ='ddoogg'
loop finished
if you want to return 'dogdog'
simply do:
def double_char(str):
return str+str
my_string = "aaabbcccaa"
required_output = "a3b2c3a2"
Please do not provide the code, I would like to try it myself, Please suggest me an approach with which i can get the required output.
As suggested by #ggradnig I've tried the below code.
def count_string(s):
current_char= s[0]
counter =0
result_string = ''
for i in range(len(s)):
if s[i] == current_char:
counter+=1
if s[i] != current_char:
result_string=result_string+current_char+str(counter)
current_char = s[i]
counter = 1
continue
result_string=result_string+current_char+str(counter)
return result_string
given_string=count_string("aabbbccccaa")
print(given_string)
Please suggest changes to improve the above code
Declare a variable for the current character you are counting and a counter variable. Set the "current character" to the first character of the string and the counter to 0. Also, declare an empty result String.
Now iterate over each character of the input String and check if it equals your "current character". If no, append this character to your result String, followed by the current value of the counter. Also, set the counter to 1 after appending and the "current character" to the character you are currently iterating over (i.e. the next character). If the character your currently iterating equals your "current character", increase your counter.
my_string = "aaabbcccaa" required_output= a3b2c3a2
You are counting consecutive characters, if the subsequent character is same, you increment a count variable, if the subsequent char is different, you move on and reinitialize count variable. Append count to the encountered character. When appending integer to a string you will have to typecast. Time complexity is O(n)
Use hashing.. with dictionaries in python. Make the character key and its occurrence a value.. And then traverse the dictionary... and took a empty string and append the both key and its value to the string.
EDIT
Now i think to change my approach instead of using sets under Dictionary and making things complex we can use simple approach. This program is working.
string2=""
string=input()
i=0
while(i<len(string)):
count=1
for j in range(i+1,len(string)):
if string[i]==string[j]:
count+=1
else:
break
string2=string2+string[i]
string2=string2+str(count)
i=i+count
print(string2)
The function groupby() in the module itertools makes this trivial. Read it's documentation where it shows the example (in comments):
>>> [list(g) for k, g in groupby('AAAABBBCCD')]
[['A', 'A', 'A', 'A'], ['B', 'B', 'B'], ['C', 'C'], ['D']]
From here you simply combine the first element of each sublist with the length of that sublist and you have your answer. Some string conversion and joining lists on empty strings necessary but basically a one-liner.
I don't understand why folks recommend the str.count() method. It combines two 'a' runs into one count which isn't desired and once you've broken the string into same letter substrings, len() can do the job.
Please suggest changes to improve the above code
from itertools import groupby
string = 'aaabbcccaa'
def count_string(s):
return ''.join(x[0] + str(len(x)) for x in (list(g) for _, g in groupby(s)))
print(string, '->', count_string(string))
Or if you wish to stay with your current algorithm, I suggest at least the following code cleanup:
def count_string(s):
counter = 0
result_string = ''
current_char = s[0]
for c in s:
if c == current_char:
counter += 1
else:
result_string += current_char + str(counter)
current_char = c
counter = 1
return result_string + current_char + str(counter)
You should use a for loop and string "count" method
and if you want me to post the code also tell me
Python has two method that you might fine useful.
I think python has count method that you could use or there is a length method that you can pass your string to and it out the length of the string.
So I was messing around in python, and developed a problem.
I start out with a string like the following:
a = "1523467aa252aaa98a892a8198aa818a18238aa82938a"
For every number, you have to add it to a sum variable.Also, with every encounter of a letter, the index iterator must move back 2. My program keeps crashing at isinstance(). This is the code I have so far:
def sum():
a = '93752aaa746a27a1754aa90a93aaaaa238a44a75aa08750912738a8461a8759383aa328a4a4935903a6a55503605350'
z = 0
for i in a:
if isinstance(a[i], int):
z = z + a[i]
elif isinstance(a[i], str):
a = a[:i] + a[(i+1):]
i = i - 2
continue
print z
return z
sum()
This part is not doing what you think:
for i in a:
if isinstance(a[i], int):
Since i is an iterator, there is no need to use a[i], it will confuse Python.
Also, since a is a string, no element of it will be an int, they will all be string. You want something like this:
for i in a:
if i.isdigit():
z += int(i)
EDIT: removing elements of an iterable while iterating over it is a common problem on SO, I would recommend creating a new string with only the elements you wan to keep:
z = 0
b = ''
for i in a:
if i.isdigit():
z += int(i)
b += str(i)
a = b # set a back to b so the "original string" is set to a string with all non-numeric characters removed.
You have a few problems with your code. You don't seem to understand how for... in loops work, but #Will already addressed that problem in his answer. Furthermore, you have a misunderstanding of how isinstance() works. As the numbers are characters of a string, when you iterate over that string each character will also be a (one-length) string. isinstance(a[i], int) will fail for every character regardless of whether or not it can be converted to an int. What you actually want to do is just try converting each character to an int and adding it to the total. If it works, great, and if not just catch the exception and keep on going. You don't need to worry about non-numeric characters because when each one raises a ValueError it will simply be ignored and the next character in the string will be processed.
string = '93752aaa746a27a1754aa90a93aaaaa238a44a75aa08750912738a8461a8759383aa328a4a4935903a6a55503605350'
def sum_(string):
total = 0
for c in string:
try:
total += int(c)
except ValueError:
pass
return total
sum_(string)
Furthermore, this function is equivalent to the following one-liners:
sum(int(c) for c in string if c.isdigit())
Or the functional style...
sum(map(int, filter(str.isdigit, string)))
So I need the output of my program to look like:
ababa
ab ba
xxxxxxxxxxxxxxxxxxx
that is it followed by a lot of spaces .
no dot at the end
The largest run of consecutive whitespace characters was 47.
But what I am getting is:
ababa
ab ba
xxxxxxxxxxxxxxxxxxx
that is it followed by a lot of spaces .
no dot at the end
The longest run of consecutive whitespace characters was 47.
When looking further into the code I wrote, I found with the print(c) statement that this happens:
['ababa', '', 'ab ba ', '', ' xxxxxxxxxxxxxxxxxxx', 'that is it followed by a lot of spaces .', ' no dot at the end']
Between some of the lines, theres the , '',, which is probably the cause of why my print statement wont work.
How would I remove them? I've tried using different list functions but I keep getting syntax errors.
This is the code I made:
a = '''ababa
ab ba
xxxxxxxxxxxxxxxxxxx
that is it followed by a lot of spaces .
no dot at the end'''
c = a.splitlines()
print(c)
#d = c.remove(" ") #this part doesnt work
#print(d)
for row in c:
print(' '.join(row.split()))
last_char = ""
current_seq_len = 0
max_seq_len = 0
for d in a:
if d == last_char:
current_seq_len += 1
if current_seq_len > max_seq_len:
max_seq_len = current_seq_len
else:
current_seq_len = 1
last_char = d
#this part just needs to count the whitespace
print("The longest run of consecutive whitespace characters was",str(max_seq_len)+".")
Regex time:
import re
print(re.sub(r"([\n ])\1*", r"\1", a))
#>>> ababa
#>>> ab ba
#>>> xxxxxxxxxxxxxxxxxxx
#>>> that is it followed by a lot of spaces .
#>>> no dot at the end
re.sub(matcher, replacement, target_string)
Matcher is r"([\n ])\1* which means:
([\n ]) → match either "\n" or " " and put it in a group (#1)
\1* → match whatever group #1 matched, 0 or more times
And the replacement is just
\1 → group #1
You can get the longest whitespace sequence with
max(len(match.group()) for match in re.finditer(r"([\n ])\1*", a))
Which uses the same matcher but instead just gets their lengths, and then maxs it.
From what I can tell, your easiest solution would be using list comprehension:
c= [item for item in a.splitlines() if item != '']
If you wish to make it slightly more robust by also removing strings that only contain whitespace such as ' ', then you can alter it as follows:
c= [item for item in a.splitlines() if item.strip() != '']
You can then also join it the list back together as follows:
output = '\n'.join(c)
This can be easily solved with the built-in filter function:
c = filter(None, a.splitlines())
# or, more explicit
c = filter(lambda x: x != "", a.splitlines())
The first variant will create a list with all elements from the list returned by a.splitlines() that do not evaluate to False, like the empty string.
The second variant creates a small anonymous function (using lambda) that checks if a given element is the empty string and returns False if that is the case. This is more explicit than the first variant.
Another option would be to use a list comprehension that achieves the same thing:
c = [string for string in a.splitlines if string]
# or, more explicit
c = [string for string in a.splitlines if string != ""]