I’ve been doing the Weekly
Challenges. The
latest
involved string mangling. (Note that this ends today.)
Task 1: Valid Tag
You are given a given a string caption for a video.
Write a script to generate tag for the given string caption in three
steps as mentioned below:
-
Format as camelCase, starting with a lower-case letter and
capitalising the first letter of each subsequent word. Merge all
words in the caption into a single string starting with a #.
-
Sanitise the String: Strip out all characters that are not English
letters (a-z or A-Z).
-
Enforce Length: If the resulting string exceeds 100 characters,
truncate it so it is exactly 100 characters long.
I did this with a very basic state machine. In Crystal:
def validtag(a)
Set up the output, and do not capitalise by default.
p = "#"
up = false
Iterate over characters.
a.chars.each do |c|
If it's a letter,
if c.letter?
Capitalise if the flag is set (and clear the flag), otherwise shift it
to lower case.
cc = c
if up
cc = cc.upcase
up = false
else
cc = cc.downcase
end
In either case, append the letter to the output.
p += cc
If it's a space and we already have more than the initial # in the
output (i.e. it's not a starting space as in ex3), flag the next
letter to be capitalised.
elsif c == ' ' && p.size > 1
up = true
end
end
Finally, if the length is greater than 100 characters, trim it.
if p.size > 100
p = p[0, 100]
end
p
end
Task 2: Group Division
You are given a string, group size and filler character.
Write a script to divide the string into groups of given size. In
the last group if the string doesn’t have enough characters
remaining fill with the given filler character.
Taking string slices varies hugely between machines (and in some
languages it was easier to take slices of a character array). One of
the easier langages is Perl:
sub groupdivision($a0, $sz, $pad) {
Pad the string. (There is a bug: consider a 2-character initial
string, a 4-character size and a 4-character pad. That will increment
forever. Better would have been to build up repetisions of the pad to
equal or exceed length sz then append it to the strhing. But all the
pads are single characters here so it doesn't arise.)
my $a = $a0;
while (length($a) % $sz != 0) {
$a .= $pad;
}
Build a regexp of the right length, and read off the captures.
my $re = '(' . '.' x $sz . ')';
[$a =~ /$re/g];
}
The more generic version, in Typst:
#let groupdivision(a0, sz, pad) = {
Pad the string.
let a = a0
while calc.rem-euclid(a.len(), sz) != 0 {
a += pad
}
Initialise the output.
let out = ()
let i = 0
While there's string left, copy a slice and increment the pointer.
while i < a.len() {
out.push(a.slice(i, count: sz))
i += sz
}
out
}
The most compact, in Crystal:
def groupdivision(a, sz, pad)
a.chars.in_groups_of(sz, pad).map{|x| x.join("")}
end
Full code on
codeberg.