Swapping every second character in a string in Python - python

I have the following problem: I would like to write a function in Python which, given a string, returns a string where every group of two characters is swapped.
For example given "ABCDEF" it returns "BADCFE".
The length of the string would be guaranteed to be an even number.
Can you help me how to do it in Python?

To add another option:
>>> s = 'abcdefghijkl'
>>> ''.join([c[1] + c[0] for c in zip(s[::2], s[1::2])])
'badcfehgjilk'

import re
print re.sub(r'(.)(.)', r'\2\1', "ABCDEF")

from itertools import chain, izip_longest
''.join(chain.from_iterable(izip_longest(s[1::2], s[::2], fillvalue = '')))
You can also use islices instead of regular slices if you have very large strings or just want to avoid the copying.
Works for odd length strings even though that's not a requirement of the question.

While the above solutions do work, there is a very simple solution shall we say in "layman's" terms. Someone still learning python and string's can use the other answers but they don't really understand how they work or what each part of the code is doing without a full explanation by the poster as opposed to "this works". The following executes the swapping of every second character in a string and is easy for beginners to understand how it works.
It is simply iterating through the string (any length) by two's (starting from 0 and finding every second character) and then creating a new string (swapped_pair) by adding the current index + 1 (second character) and then the actual index (first character), e.g., index 1 is put at index 0 and then index 0 is put at index 1 and this repeats through iteration of string.
Also added code to ensure string is of even length as it only works for even length.
string = "abcdefghijklmnopqrstuvwxyz123"
# use this prior to below iteration if string needs to be even but is possibly odd
if len(string) % 2 != 0:
string = string[:-1]
# iteration to swap every second character in string
swapped_pair = ""
for i in range(0, len(string), 2):
swapped_pair += (string[i + 1] + string[i])
# use this after above iteration for any even or odd length of strings
if len(swapped_pair) % 2 != 0:
swapped_adj += swapped_pair[-1]
print(swapped_pair)
badcfehgjilknmporqtsvuxwzy21 # output if the "needs to be even" code used
badcfehgjilknmporqtsvuxwzy213 # output if the "even or odd" code used

Here's a nifty solution:
def swapem (s):
if len(s) < 2: return s
return "%s%s%s"%(s[1], s[0], swapem (s[2:]))
for str in ("", "a", "ab", "abcdefgh", "abcdefghi"):
print "[%s] -> [%s]"%(str, swapem (str))
though possibly not suitable for large strings :-)
Output is:
[] -> []
[a] -> [a]
[ab] -> [ba]
[abcdefgh] -> [badcfehg]
[abcdefghi] -> [badcfehgi]

If you prefer one-liners:
''.join(reduce(lambda x,y: x+y,[[s[1+(x<<1)],s[x<<1]] for x in range(0,len(s)>>1)]))

Here's a another simple solution:
"".join([(s[i:i+2])[::-1]for i in range(0,len(s),2)])

Related

How does comparing two chars (within a string) work in Python

I am starting to learn Python and looked at following website: https://www.w3resource.com/python-exercises/string/
I work on #4 which is "Write a Python program to get a string from a given string where all occurrences of its first char have been changed to '$', except the first char itself."
str="restart"
char=str[0]
print(char)
strcpy=str
i=1
for i in range(len(strcpy)):
print(strcpy[i], "\n")
if strcpy[i] is char:
strcpy=strcpy.replace(strcpy[i], '$')
print(strcpy)
I would expect "resta$t" but the actual result is: $esta$t
Thank you for your help!
There are two issues, first, you are not starting iteration where you think you are:
i = 1 # great, i is 1
for i in range(5):
print(i)
0
1
2
3
4
i has been overwritten by the value tracking the loop.
Second, the is does not mean value equivalence. That is reserved for the == operator. Simpler types such as int and str can make it seem like is works in this fashion, but other types do not behave this way:
a, b = 5, 5
a is b
True
a, b = "5", "5"
a is b
True
a==b
True
### This doesn't work
a, b = [], []
a is b
False
a == b
True
As #Kevin pointed out in the comments, 99% of the time, is is not the operator you want.
As far as your code goes, str.replace will replace all instances of the argument supplied with the second arg, unless you give it an optional number of instances to replace. To avoid replacing the first character, grab the first char separately, like val = somestring[0], then replace the rest using a slice, no need for iteration:
somestr = 'restart' # don't use str as a variable name
val = somestr[0] # val is 'r'
# somestr[1:] gives 'estart'
x = somestr[1:].replace(val, '$')
print(val+x)
# resta$t
If you still want to iterate, you can do that over the slice as well:
# collect your letters into a list
letters = []
char = somestr[0]
for letter in somestr[1:]: # No need to track an index here
if letter == char: # don't use is, use == for value comparison
letter = '$' # change letter to a different value if it is equal to char
letters.append(letter)
# Then use join to concatenate back to a string
print(char + ''.join(letters))
# resta$t
There are some need of modification on your code.
Modify your code with as given in below.
strcpy="restart"
i=1
for i in range(len(strcpy)):
strcpy=strcpy.replace(strcpy[0], '$')[:]
print(strcpy)
# $esta$t
Also, the best practice to write code in Python is to use Function. You can modify your code as given below or You can use this function.
def charreplace(s):
return s.replace(s[0],'$')[:]
charreplace("restart")
#'$esta$t'
Hope this helpful.

counting the number of substrings in a string

I am working on an Python assignment and I am stuck here.
Apparently, I have to write a code that counts the number of a given substring within a string.
I thought I got it right, then I am stuck here.
def count(substr,theStr):
# your code here
num = 0
i = 0
while substr in theStr[i:]:
i = i + theStr.find(substr)+1
num = num + 1
return num
substr = 'is'
theStr = 'mississipi'
print(count(substr,theStr))
if I run this, I expect to get 2 as the result, rather, I get 3...
See, other examples such as ana and banana works fine, but this specific example keeps making the error. I don't know what I did wrong here.
Would you PLEASE help me out.
In your code
while substr in theStr[i:]:
correctly advances over the target string theStr, however the
i = i + theStr.find(substr)+1
keeps looking from the start of theStr.
The str.find method accepts optional start and end arguments to limit the search:
str.find(sub[, start[, end]])
Return the lowest index in the string where substring sub is found
within the slice s[start:end]. Optional arguments start and end
are interpreted as in slice notation. Return -1 if sub is not found.
We don't really need to use in here: we can just check that find doesn't return -1. It's a bit wasteful performing an in search when we then need to repeat the search using find to get the index of the substring.
I assume that you want to find overlapping matches, since the str.count method can find non-overlapping matches, and since it's implemented in C it's more efficient than implementing it yourself in Python.
def count(substr, theStr):
num = i = 0
while True:
j = theStr.find(substr, i)
if j == -1:
break
num += 1
i = j + 1
return num
print(count('is', 'mississipi'))
print(count('ana', 'bananana'))
output
2
3
The core of this code is
j = theStr.find(substr, i)
i is initialised to 0, so we start searching from the beginning of theStr, and because of i = j + 1 subsequent searches start looking from the index following the last found match.
The code change you need is -
i = i + theStr[i:].find(substr)+ 1
instead of
i = i + theStr.find(substr)+ 1
In your code the substring is always found until i reaches position 4 or more. But while finding the index of the substring, you were using the original(whole) string which in turn returns the position as 1.
In your example of banana, after first iteration i becomes 2. So, in next iteration str[i:] becomes nana. And the position of substring ana in this sliced string and the original string is 1. So, the bug in the code is just suppressed and the code seems to work fine.
If your code is purely for learning purpose, the you can do this way. Otherwise you may want to make use of python provided functions (like count()) to do the job.
Counting the number of substrings:
def count(substr,theStr):
num = 0
for i in range(len(theStr)):
if theStr[i:i+len(substr)] == substr:
num += 1
return num
substr = 'is'
theStr = 'mississipi'
print(count(substr,theStr))
O/P : 2
where theStr[i:i+len(substr)] is slice string, i is strating index and i+len(substr) is ending index.
Eg.
i = 0
substr length = 2
first-time compare substring is => mi
String slice more details

How to make this simple string function "pythonic"

Coming from the C/C++ world and being a Python newb, I wrote this simple string function that takes an input string (guaranteed to be ASCII) and returns the last four characters. If there’s less than four characters, I want to fill the leading positions with the letter ‘A'. (this was not an exercise, but a valuable part of another complex function)
There are dozens of methods of doing this, from brute force, to simple, to elegant. My approach below, while functional, didn’t seem "Pythonic".
NOTE: I’m presently using Python 2.6 — and performance is NOT an issue. The input strings are short (2-8 characters), and I call this function only a few thousand times.
def copyFourTrailingChars(src_str):
four_char_array = bytearray("AAAA")
xfrPos = 4
for x in src_str[::-1]:
xfrPos -= 1
four_char_array[xfrPos] = x
if xfrPos == 0:
break
return str(four_char_array)
input_str = "7654321"
print("The output of {0} is {1}".format(input_str, copyFourTrailingChars(input_str)))
input_str = "21"
print("The output of {0} is {1}".format(input_str, copyFourTrailingChars(input_str)))
The output is:
The output of 7654321 is 4321
The output of 21 is AA21
Suggestions from Pythoneers?
I would use simple slicing and then str.rjust() to right justify the result using A as fillchar . Example -
def copy_four(s):
return s[-4:].rjust(4,'A')
Demo -
>>> copy_four('21')
'AA21'
>>> copy_four('1233423')
'3423'
You can simple adding four sentinel 'A' character before the original string, then take the ending four characters:
def copy_four(s):
return ('AAAA'+s)[-4:]
That's simple enough!
How about something with string formatting?
def copy_four(s):
return '{}{}{}{}'.format(*('A'*(4-len(s[-4:])) + s[-4:]))
Result:
>>> copy_four('abcde')
'bcde'
>>> copy_four('abc')
'Aabc'
Here's a nicer, more canonical option:
def copy_four(s):
return '{:A>4}'.format(s[-4:])
Result:
>>> copy_four('abcde')
'bcde'
>>> copy_four('abc')
'Aabc'
You could use slicing to get the last 4 characters, then string repetition (* operator) and concatenation (+ operator) as below:
def trailing_four(s):
s = s[-4:]
s = 'A' * (4 - len(s)) + s
return s
You can try this
def copy_four_trailing_chars(input_string)
list_a = ['A','A','A','A']
str1 = input_string[:-4]
if len(str1) < 4:
str1 = "%s%s" % (''.join(list_a[:4-len(str1)]), str1)
return str1

moving integers from one string to another?

I am looking at adding numbers to a string as python reads through a string.
So if I had a string a = "253+"
I would then have an empty string b.
So, how would I have python read the 2, add it to string b, then read the 5 and add it to string b, and then add the 5 and add it to string b, when it hits something that isnt an integer though, it stops the function.
then string b would be b = "253"
is there a specific call in an iteration that would ask for integers and then add i to another string?
tl;dr
I want to use an iteration to add numbers from one string to another, which stops when it reaches a non-integer.
string b would be an empty string, and string a would be a="253+"
after the call would be done, strng b would equal b="253"
I know this sounds like a homework question, but its not. If you need anything else clarified, I would be happy to.
Here is a simple method for extracting the digits from a string:
In [13]: a="253+"
In [14]: ''.join(c for c in a if c.isdigit())
Out[14]: '253'
The question is a bit unclear, but is this what you're looking for?
a = "123+"
b=""
for c in a:
try:
int(c)
b = b + c
except ValueError:
print 'This is not an int ' + c
break
Running this results in this b being 123 and breaking on the + character. It sounds like the part that's tricky for you at the moment is the try..except ValueError bit. Not that I don't have to break the loop when a ValueError happens, I could just keep looping over the remaining characters in the string and ignore ones that cannot be parsed into an int
In [201]: import itertools as IT
In [202]: a = "253+9"
In [203]: ''.join(IT.takewhile(str.isdigit, a))
Out[203]: '253'
IT.takewhile will stop at the first character in a which is not a digit.
Another way would be to use a regex pattern. You could split the string on non-digits using the pattern r'\D':
In [208]: import re
In [209]: re.split(r'\D', a, maxsplit=1)[0]
Out[209]: '253'
With the use of the for loop, this is relatively easy. If we use our ASCII knowledge, we know that the ASCII values of the digits range from 48 (which represents 0 as a string) to 57 (which represents 9 as a string).
We can find the ASCII value of a character by using the built in method ord(x) where x is the character (i.e. ord('4') is equal to 52, the integer).
Now that we have this knowledge, it will be easy to add this to our for-loop. We simply make a for-loop that goes from 0 to the length of the string minus 1. In the for loop, we are going to use the iteration that we are on as an index, find the character at that index in our string, and finally check to see if its ord value falls in the range that we want.
This will look something like this:
def method(just_a_variable):
b = ''
for i in range(0, len(a)):
if (#something):
if (#something):
b = b+a[i]
return b
Can you fill in the "#somethings"?
Try this:
a = "i889i" #Initial value of A
b = "" #Empty string to store result into
for each in a: #iterate through all characters of a
if each.isdigit(): #check if the current character is a digit
b += each #append to b if the current character is a digit
else: #if current character is NOT a digit
break #break out of for loop
print b #print out result
Hope this helps!
You can write a generator with a regex and generate them one by one:
>>> import re
>>> s='123+456abc789'
>>> nums=(m.group(1) for m in re.finditer(r'(\d+)', s))
>>> next(nums)
'123'
>>> next(nums)
'456'
>>> next(nums)
'789'

Subscripting an Array of Integers in Python?

I'm absolutely terrible at Python and my Computer Programming class ends in two days (thank god), but I am having the hardest time figuring out what is probably the easiest code ever.
The instructions to my assignment state, "Write a program which reads in text until a '!' is found. Use an array of integers subscripted by the letters 'A' through 'Z'."
From what i have done so far:
msg = input("What is your message? ")
msg = msg.upper()
int_array = [0] * 26
for alph in range (65, 91):
char = chr(alph)
print(char)
(int_array[char])
any help would be greatly appreciated! thanks!
EDIT: This is the full assignment:
Write a program which reads in text from the keyboard until a ('!') is found.
Using an array of integers subscripted by the letters 'A' through 'Z', count the number occurrences of each letter (regardless of whether it is upper or lower case). In a separate counter, also count the total number of "other" characters ('.', '?', ' ', '2', etc.).
Print out the count for each letter found. Also, print the count of the non-letter characters.
By inspecting the array, print out the count of the number of vowels, and the number of consonants.
Print out which letter was found the most times. (Note there may be more than one letter which has the maximum count attached to it.) Print out which letter (or letters) was found the least number of times, but make certain to exclude letters which were not found at all.
UPDATE:
I have gotten this far with my code
msg = input("What is your message? ")
print ()
num_alpha = 26
int_array = [0] * num_alpha
for alpha in range(num_alpha):
int_array[alpha] = chr(alpha + 65)
print(int_array[alpha], end = "")
print()
lett = 0
otherch = 0
num_vowels = 0
num_consanants = 0
count_character = [0] * 100000
length = len(msg)
for character in msg.upper():
if character == "!":
print("lett =", lett)
print("other char = ", otherch)
print("num_vowels = ", num_vowels)
print("num_consanants = ", num_consanants)
elif character < "A" or letter > "Z":
otherch = otherch + 1
count_character[ord(character)] = count_character[ord(character)] + 1
else:
lett = lett + 1
count_character[ord(character)] = count_character[ord(character)] + 1
for character in msg:
print("character", character, "appeared" , count_character[ord(character)] , "times")
it's obviously not finished yet, but every time i print the last print statement, it says that each character appeared 0 times. can anybody help?
You're going to need to get clarification on this, because there's no such thing as "an array of integers subscripted by the letters 'A' through 'Z'" in Python.
Possible interpretations that I can think of:
It's supposed to be a dictionary rather than an array. Python dictionaries are similar to lists (the Python datatype that is roughly equivalent to "arrays" in other languages), but they can be subscripted by strings, whereas lists can be subscripted only by integers. This way, you can store an integer to be associated with each letter. This is how most Python programmers would generally do something like this.
You're supposed to use parallel lists. You can do this by making two lists of 26 elements each, one containing the letters 'A' through 'Z' and one containing integers. For each letter, you could then use the list.index method to find the index in the first list where that letter is, then look up that index in the second list. (In theory, you wouldn't really need the first list, since Python strings are like lists in that they can be subscripted with integers and support the index method. So you could use the string 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' instead of a list. Or you could use the ord function, which is the inverse of the chr function. But I don't know if you're supposed to use these features.)
I'm not 100% sure the following is right because I agree with the others that the assignment description is wonky. It looks like a C-based homework assignment lazily ported to Python. That said:
In principle rather than hardcoding the bounds of the alphabet I'd go with ord('A') and ord('Z')+1, so that I can say something like alphabet = list(range(ord('A'), ord('Z')+1))
Renaming int_array to counter might make it more obvious what you need to do in your inner loop (keeping in mind that you're using the letters as your indices. Or rather, you'd need something more like ord(letter)-ord('A') as your indices)
You don't want to loop over the alphabet; you want to loop over the input.
count should be initialized to [0]*27 to track "other" values. You can increment counter[-1] for non-alphabetic characters.
Your final value is chr of counter.index(max(counter)). You may find it more straightforward, or your teacher may find it more acceptable, to just write a for loop.

Categories

Resources