How to group a section of numbers in a list together Python
Question:
I am currently working on a program in which I must take a string as input and then reverse all of the numbers in that string, leaving all other characters the same. I managed to do this, however it seems that I must reverse sections of numbers at a time, and not reverse every number. I am not sure how I can do this with my solution. I would prefer not to use any libraries.
For example:
For input abc123abc456abc7891
My result: abc198abc765abc4321
Target Result: abc321abc654abc1987
Here is what I have:
#Fucntion just reverses the numbers that getn gives to it
def reverse(t):
t = t[::-1]
return t
def getn(w):
w = list(w)
Li = []
#Going through each character of w(the inputted string) and adding any numbers to the list Li
for i in w:
if i.isdigit():
Li.append(i)
#Turn Li back into a string so I can then reverse it using the above function
#after reversing, I turn it back into a list
Li = ''.join(Li)
Li = reverse(Li)
Li = list(Li)
#I use t only for the purpose of the for loop below,
#to get the len of the string,
#a is used to increment the position in Li
t = ''.join(w)
a = 0
#This goes through each position of the string again,
#and replaces each of the original numbers with the reversed sequence
for i in range(0,len(t)):
if w[i].isdigit():
w[i] = Li[a]
a+=1
#Turn w back into a string to print
w = ''.join(w)
print('New String:n'+w)
x = input('Enter String:n')
getn(x)
Answers:
The following isn’t particularly elegant, but is conceptually fairly simple, using groupby
from itertools
to split the string up into the groups of digits or non-digits (this is Python 3.7, I think it should work on any Python 3.x but haven’t tested except on 3.7):
from itertools import groupby
def getn(s):
sections = groupby(s, key=lambda char: char.isdigit())
result = []
for isdig, chars in sections:
if isdig:
result += list(reversed(list(chars)))
else:
result += list(chars)
return "".join(result)
input = "abc123abc456abc7891"
print(getn(input))
Solution outline:
- Break the string into a list of substrings. Each substring is defined by the division between digits and non-digits. Your result at othe end of this stage should be
["abc", "123", "abc", "456", "abc", "7891"]
- Go through this list; replace each digit string with its reverse.
join
this list into a single string.
The final step is simply ''.join(substring_list)
.
The middle step is contained in what you’re already doing.
The first step isn’t trivial, but well within the coding ability in your original post.
Can you take it from here?
UPDATE
Here’s the logic to break the string into groups as needed.
Check the “digit-ness” of each char. If it’s different from that of the previous char, then you have to start a new substring.
instr = "abc123abc456abc7891"
substr = ""
sub_list = []
prev_digit = instr[0].isdigit()
for char in instr:
# if the character's digit-ness is different from the last one,
# then "tie off" the current substring and start a new one.
this_digit = char.isdigit()
if this_digit != prev_digit:
sub_list.append(substr)
substr = ""
prev_digit = this_digit
substr += char
# Add the last substr to the list
sub_list.append(substr)
print(sub_list)
Output:
['abc', '123', 'abc', '456', 'abc', '7891']
Idea is to separate numbers and letters into substring sections. Iterate through each section and reverse only the integer sections. Then join the sections into a single string.
def getn(w):
ans = []
section = ''
if w[0].isdigit():
last = 'digit'
else:
last = 'letter'
for char in w:
if char.isdigit():
if last == 'letter':
ans.append(section)
section = ''
last = 'digit'
section += char
else:
if last == 'digit':
ans.append(section)
section = ''
last = 'letter'
section += char
ans.append(section)
for index, section in enumerate(ans):
if section.isdigit():
ans[index] = section[::-1]
return ''.join(ans)
string = 'abc123abc456abc7891'
print(getn(string))
This is a working code based on @prune suggestion.
def type_changed(s1,s2):
if s1.isdigit() != s2.isdigit():
return True
def get_subs(s):
sub_strings = list()
i = 0
start = i
while i < len(s):
if i == len(s)-1:
sub_strings.append(s[start:i+1])
break
if type_changed(s[i], s[i+1]):
sub_strings.append(s[start:i+1])
start = i+1
i +=1
return sub_strings
def reverse(subs):
for i, sub in enumerate(subs):
if sub.isdigit():
subs[i] = subs[i][::-1]
return ''.join(subs)
test_strings = [
'abc123abc456abc7891b',
'abc123abc456abc7891',
'abc123abc456abc7891abc',
'a1b2c3d4e5f6',
'a1b2c3d4e5f6g',
'1234',
'abcd',
'1',
'a'
]
for item in test_strings:
print(item)
print(reverse(get_subs(item)))
print
using regex substitute function
import re
instr = "abc123abc456abc7891"
result = re.sub('d+', lambda x : ''.join(reversed(str(x[0]))), instr)
print(result)
"abc321abc654abc1987"
I am currently working on a program in which I must take a string as input and then reverse all of the numbers in that string, leaving all other characters the same. I managed to do this, however it seems that I must reverse sections of numbers at a time, and not reverse every number. I am not sure how I can do this with my solution. I would prefer not to use any libraries.
For example:
For input abc123abc456abc7891
My result: abc198abc765abc4321
Target Result: abc321abc654abc1987
Here is what I have:
#Fucntion just reverses the numbers that getn gives to it
def reverse(t):
t = t[::-1]
return t
def getn(w):
w = list(w)
Li = []
#Going through each character of w(the inputted string) and adding any numbers to the list Li
for i in w:
if i.isdigit():
Li.append(i)
#Turn Li back into a string so I can then reverse it using the above function
#after reversing, I turn it back into a list
Li = ''.join(Li)
Li = reverse(Li)
Li = list(Li)
#I use t only for the purpose of the for loop below,
#to get the len of the string,
#a is used to increment the position in Li
t = ''.join(w)
a = 0
#This goes through each position of the string again,
#and replaces each of the original numbers with the reversed sequence
for i in range(0,len(t)):
if w[i].isdigit():
w[i] = Li[a]
a+=1
#Turn w back into a string to print
w = ''.join(w)
print('New String:n'+w)
x = input('Enter String:n')
getn(x)
The following isn’t particularly elegant, but is conceptually fairly simple, using groupby
from itertools
to split the string up into the groups of digits or non-digits (this is Python 3.7, I think it should work on any Python 3.x but haven’t tested except on 3.7):
from itertools import groupby
def getn(s):
sections = groupby(s, key=lambda char: char.isdigit())
result = []
for isdig, chars in sections:
if isdig:
result += list(reversed(list(chars)))
else:
result += list(chars)
return "".join(result)
input = "abc123abc456abc7891"
print(getn(input))
Solution outline:
- Break the string into a list of substrings. Each substring is defined by the division between digits and non-digits. Your result at othe end of this stage should be
["abc", "123", "abc", "456", "abc", "7891"]
- Go through this list; replace each digit string with its reverse.
join
this list into a single string.
The final step is simply ''.join(substring_list)
.
The middle step is contained in what you’re already doing.
The first step isn’t trivial, but well within the coding ability in your original post.
Can you take it from here?
UPDATE
Here’s the logic to break the string into groups as needed.
Check the “digit-ness” of each char. If it’s different from that of the previous char, then you have to start a new substring.
instr = "abc123abc456abc7891"
substr = ""
sub_list = []
prev_digit = instr[0].isdigit()
for char in instr:
# if the character's digit-ness is different from the last one,
# then "tie off" the current substring and start a new one.
this_digit = char.isdigit()
if this_digit != prev_digit:
sub_list.append(substr)
substr = ""
prev_digit = this_digit
substr += char
# Add the last substr to the list
sub_list.append(substr)
print(sub_list)
Output:
['abc', '123', 'abc', '456', 'abc', '7891']
Idea is to separate numbers and letters into substring sections. Iterate through each section and reverse only the integer sections. Then join the sections into a single string.
def getn(w):
ans = []
section = ''
if w[0].isdigit():
last = 'digit'
else:
last = 'letter'
for char in w:
if char.isdigit():
if last == 'letter':
ans.append(section)
section = ''
last = 'digit'
section += char
else:
if last == 'digit':
ans.append(section)
section = ''
last = 'letter'
section += char
ans.append(section)
for index, section in enumerate(ans):
if section.isdigit():
ans[index] = section[::-1]
return ''.join(ans)
string = 'abc123abc456abc7891'
print(getn(string))
This is a working code based on @prune suggestion.
def type_changed(s1,s2):
if s1.isdigit() != s2.isdigit():
return True
def get_subs(s):
sub_strings = list()
i = 0
start = i
while i < len(s):
if i == len(s)-1:
sub_strings.append(s[start:i+1])
break
if type_changed(s[i], s[i+1]):
sub_strings.append(s[start:i+1])
start = i+1
i +=1
return sub_strings
def reverse(subs):
for i, sub in enumerate(subs):
if sub.isdigit():
subs[i] = subs[i][::-1]
return ''.join(subs)
test_strings = [
'abc123abc456abc7891b',
'abc123abc456abc7891',
'abc123abc456abc7891abc',
'a1b2c3d4e5f6',
'a1b2c3d4e5f6g',
'1234',
'abcd',
'1',
'a'
]
for item in test_strings:
print(item)
print(reverse(get_subs(item)))
print
using regex substitute function
import re
instr = "abc123abc456abc7891"
result = re.sub('d+', lambda x : ''.join(reversed(str(x[0]))), instr)
print(result)
"abc321abc654abc1987"