Add border(*) for rectangular matrix of characters - python

I am trying to add * as borders for rectangular matrix of characters. For example,
["abc", "ded"]
should return
["*****","*abc*","*ded*","*****"]
What I did was I create a new matrix with 2 more rows and columns than the original one, and I filled it with *. So the problem is when I am replacing the * inside with original letters, I have an error of out of index. I couldn't quite figure out why?
def addBorder(picture):
m=len(picture) #number of rows
n=len(picture[0]) #num of columns
newpic=[['*'*(n+2)]for y in range(m+2)]
for x in range(1,m+1):
for y in range(1,n+1):
newpic[x][y]=picture[x-1][y-1]
return newpic

Strings are immutable, so you can not "edit" single characters inside them bey indexing into them - thats what you are trying in your for x ..: for y: ... loop.
To keep most of your code, you can change it to:
def addBorder(picture):
m=len(picture) #number of rows
n=len(picture[0]) #num of columns
newpic=[['*'*(n+2)]for y in range(m+2)]
for idx,text in enumerate(picture): # get text and index here
newpic[idx+1] = '*' + text+ '*' # change text in +1 row in target list
return newpic
print(addBorder( ["abc", "ded"]))
Output:
[['*****'], '*abc*', '*ded*', ['*****']]
Changing more code:
def addBorder(picture):
# slightly more compley length computation, will work for ragged text as well
maxL = max(len(x) for x in picture)
patt = "*{:<"+str(maxL)+"}*" # left justified by maxL , see link below
rv = []
rv.append('*'*(maxL+2)) # top border
for t in picture:
rv.append(patt.format(t)) # text + adornment
rv.append('*'*(maxL+2)) # bottom border
return rv
print(addBorder( ["abc", "defgh","i"]))
Output:
['*******',
'*abc *',
'*defgh*',
'*i *',
'*******']
Link: string format mini language
Your out of index error message is somewhat misleading - you are inside the bounds of your lists, but you are trying to manipulate the string - I would have thought a 'str' object does not support item assignment would be more appropriate here...
Edit: see Azats answer for why your error occurs - I left text in so his post does not loose its reference.

If I understood correctly, you are trying to
Create filled with * newpic.
Modify newpic[1:-1] and replace * (excluding borders) with picture elements contents.
This approach has a problem with str objects immutability, but even if they were mutable this seems to be inefficient to create **...** string and then mutate them character-by-character.
About your error: it isn't misleading as stated by #PatrickArtner, it originates from a typo (I guess), because you are creating lists of list of str:
>>> m = 3
>>> n = 4
>>> [['*'*(n+2)]for y in range(m+2)]
[['******'], ['******'], ['******'], ['******'], ['******']]
so when y equals to 1 you are getting this error (because each of newpic sublists has single str element inside of them).
Instead of trying to modify list of str we can create list and append str to it like
def addBorder(picture,
border_size=1):
max_substring_length = max(map(len, picture))
# top border
result = ['*' * (max_substring_length + border_size * 2)]
for substring in picture:
diff = max_substring_length - len(substring)
additional_length, extra = divmod(diff, 2)
# handling non-equivalent case
prepend = '*' * (border_size + additional_length + extra)
append = '*' * (border_size + additional_length)
result.append(prepend + substring + append)
# bottom border
result.append('*' * (max_substring_length + border_size * 2))
return result
Test
for string in addBorder(["abc", "ded"]):
print(string)
gives us
*****
*abc*
*ded*
*****
non-equivalent by size case
for string in addBorder(["abc", "deed"]):
print(string)
gives us
******
**abc*
*deed*
******

In C++
std::vector<std::string> addBorder(std::vector<std::string> picture)
{
for(auto &p: picture)
p = "*"+p+"*";
picture.insert(picture.begin(), string(picture[0].size(),'*'));
picture.insert(picture.end(), string(picture[0].size(),'*'));
return picture;
}

Related

I need to make a map (literally not the function) in which i should print a 20*20 map with pluses(+) but mark some of the coordinates

I need to make a map (literally not the function) in which I should print a 20*20 map with pluses(+) but mark some of the coordinates which are inputs with #
first, I ask how many coordinates the user wants to give me
second, I get the coordinates and print the map with said coordinates marked on it
e.g. :
input:
2
(10,11)
(10,12)
output :
# + + + + + + +
c= ( 10 , 11)
a=c\[0\]
b=c\[1\]
for y in range (20):
if y == a :
for x in range (20):
if x==b:
print('#' , end= ' ')
else :
print('+' , end = ' ')
else:
for x in range (20):
print('+' , end= ' ')
print()
This should do the trick:
from typing import List
ROWS = COLS = 20
# Define a function to create rows
def create_row(num_cols: int, default_char: str = "+")->List[str]:
return [default_char for i in range(num_cols)]
# Define a function to create the map using function for row creation
# Make the arguments keyword only, it does matter which int is rows, which is cols (as long as your matrix is not necessarily n x n, but n x m (in general)
def create_map(*,rows:int, cols:int, default_char:str = "+")->List[List[str]]:
char_map = []
for i in range(rows):
char_map.append(create_row(num_cols=cols, default_char=default_char))
return char_map
# Create the map
plus_map = create_map(rows=ROWS, cols=COLS)
# This is just an example of user input coordinates
hashtag_coordinates = [(1,2), (3,4)] # I'll leave this part for you, populate it as you wish (using input function or whatever, file reading etc.), just keep it in a form of list of tuples
# Substitute the default "+" with "#" for user input coordinates
for x,y in hashtag_coordinates:
plus_map[x][y] = "#"
# Print your map row by row
for r in plus_map:
print(r)
add docstrings, docstrings are cool...
think of good variable names, those above are ok'ish, probably can be better / more descriptive
[OPTIONAL] wrap the substitution of "+" into "#" in another function
[OPTIONAL] write your own print function if the output is not what you expect it to be
do not name variables "a", "b", cause that means nothing
if you repeat the same magic number in a few places like in range(20) and so on name it, all caps if it's a constant

How can I split a string on the Nth character before a given parameter (Python)

Usually I use the .split() method to split strings on a dataframe, but this time I have a string that looks like this:
"3017381ª Série - EM"
or
"3017381º Ano - Iniciais"
And I'd like to find "º" or "ª" and then split it 2 characters before. The string should look like this after the split:
["301728","1ª Série - EM"]
You could use the str.find function, and alter the location in the string you received.
in a function it would look like this (do consider it will split the string on the first entry of the options defined in charList, but you could extend the methodology if required for multiple splits):
def splitString(s,offset=0,charList=[]):
for opt in charList:
x = s.find(opt)
if x != -1: # default when not found
return [s[:x+offset],s[x+offset:]]
return [s] # input char not found
You can then call the function:
splitString("3017381ª Série - EM",offset=-1,charList=[ "º","ª"])
Maybe it's a little bit confusing but the result it's the one you want.
a = "3017381ª Série - EM"
print(a[7])
print(a[5])
b = a[7]
c = a[5]
print(b)
print(c)
x = a.split(b)
y = a.split(c)
newY = y[1]
newX = x[0]
print(newY)
print(newX)
minusX = newX[:-1]
print(minusX)
z =[]
z.append(minusX)
z.append(y[1])
print("update:", z)

ArcGIS:Python - Adding Commas to a String

In ArcGIS I have intersected a large number of zonal polygons with another set and recorded the original zone IDs and the data they are connected with. However the strings that are created are one long list of numbers ranging from 11 to 77 (each ID is 11 characters long). I am looking to add a "," between each one making, it easier to read and export later as a .csv file. To do this I wrote this code:
def StringSplit(StrO,X):
StrN = StrO #Recording original string
StrLen = len(StrN)
BStr = StrLen/X #How many segments are inside of one string
StrC = BStr - 1 #How many times it should loop
if StrC > 0:
while StrC > 1:
StrN = StrN[ :((X * StrC) + 1)] + "," + StrN[(X * StrC): ]
StrC = StrC - 1
while StrC == 1:
StrN = StrN[:X+1] + "," + StrN[(X*StrC):]
StrC = 0
while StrC == 0:
return StrN
else:
return StrN
The main issue is how it has to step through multiple rows (76) with various lengths (11 -> 77). I got the last parts to work, just not the internal loop as it returns an error or incorrect outputs for strings longer than 22 characters.
Thus right now:
1. 01234567890 returns 01234567890
2. 0123456789001234567890 returns 01234567890,01234567890
3. 012345678900123456789001234567890 returns either: Error or ,, or even ,,01234567890
I know it is probably something pretty simple I am missing, but I can't seem remember what it is...
It can be easily done by regex.
those ........... are 11 dots for give split for every 11th char.
you can use pandas to create csv from the array output
Code:
import re
x = re.findall('...........', '01234567890012345678900123456789001234567890')
print(x)
myString = ",".join(x)
print(myString)
output:
['01234567890', '01234567890', '01234567890', '01234567890']
01234567890,01234567890,01234567890,01234567890
for the sake of simplicity you can do this
code:
x = ",".join(re.findall('...........', '01234567890012345678900123456789001234567890'))
print(x)
Don't make the loops by yourself, use python libraries or builtins, it will be easier. For example :
def StringSplit(StrO,X):
substring_starts = range(0, len(StrO), X)
substrings = (StrO[start:start + X] for start in substring_starts)
return ','.join(substrings)
string = '1234567890ABCDE'
print(StringSplit(string, 5))
# '12345,67890,ABCDE'

Regarding the behavior of string padding using str.ljust in python

I have a use case in which I am setting the labels for
matplotlib.colorbar
and I want to distribute the description evenly using the code below
temp = ""
for section in range(total_section_len):
temp.ljust(60//(total_section_len * 5))
temp += "stream 0"
temp.ljust(60//(total_section_len * 5))
temp += "stream 1"
temp.ljust(60//(total_section_len * 5))
print temp
I am expecting something like
" stream0 stream1 "
but instead what I get is
"stream0stream1"
Why does
str.ljust
behavior in such fashion?
Thanks
The parameter to ljust is the "minimum" length of the string. It will only pad with spaces if this value is longer than the current length of the string.
Unless total_section_len is only 1, 60//(total_section_len * 5) will be less than the length of your strings, so no padding is taking place.
Also, it doesn't modify the string in place as strings are immutable. You need to use the return value, i.e. temp = temp.ljust(...)
ljust(width, fillchr) returns a left justified string by width specified and fills it with fillchr, whose default is empty space.
So
a = ''
b = a.ljust(10)
print(a)
>>> ''
print(b)
>>> ' '
It returns a left justified string to variable b and variable a is not modified.
and So this piece of code should work:
a = ''
a += ''.ljust(10) + 'stream0'
a += ''.ljust(10) + 'stream1'
print(a)
>>> ' stream0 stream1'

How to make a list of lists in python

I am trying to make a list of lists in python using random.random().
def takeStep(prevPosition, maxStep):
"""simulates taking a step between positive and negative maxStep, \
adds it to prevPosition and returns next position"""
nextPosition = prevPosition + (-maxStep + \
( maxStep - (-maxStep)) * random.random())
list500Steps = []
list1000Walks = []
for kk in range(0,1000):
list1000Walks.append(list500Steps)
for jj in range(0 , 500):
list500Steps.append(list500Steps[-1] + takeStep(0 , MAX_STEP_SIZE))
I know why this gives me what it does, just don't know how to do something about it. Please give the simplest answer, in new at this and don't know a lot yet.
for kk in xrange(0,1000):
list500steps = []
for jj in range(0,500):
list500steps.append(...)
list1000walks.append(list500steps)
Notice how I am creating an empty array (list500steps) each time in the first for loop? Then, after creating all the steps I append that array (Which is now NOT empty) to the array of walks.
import random
def takeStep(prevPosition, maxStep):
"""simulates taking a step between positive and negative maxStep, \
adds it to prevPosition and returns next position"""
nextPosition = prevPosition + (-maxStep + \
( maxStep - (-maxStep)) * random.random())
return nextPosition # You didn't have this, I'm not exactly sure what you were going for #but I think this is it
#Without this statement it will repeatedly crash
list500Steps = [0]
list1000Walks = [0]
#The zeros are place holders so for the for loop (jj) below. That way
#The first time it goes through the for loop it has something to index-
#during "list500Steps.append(list500Steps[-1] <-- that will draw an eror without anything
#in the loops. I don't know if that was your problem but it isn't good either way
for kk in range(0,1000):
list1000Walks.append(list500Steps)
for jj in range(0 , 500):
list500Steps.append(list500Steps[-1] + takeStep(0 , MAX_STEP_SIZE))
#I hope for MAX_STEP_SIZE you intend on (a) defining the variable (b) inputing in a number
You might want to print one of the lists to check the input.

Categories

Resources