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)
Asked By: Demons

||

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))
Answered By: Robin Zigmond

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']
Answered By: Prune

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))
Answered By: nathancy

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
Answered By: Zaman

using regex substitute function

import re

instr = "abc123abc456abc7891"

result = re.sub('d+', lambda x : ''.join(reversed(str(x[0]))), instr)

print(result)

"abc321abc654abc1987"

Answered By: Gaurav Verma
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.