loading...

Daily Challenge #15 - Stop gninnipS My sdroW!

thepracticaldev profile image dev.to staff ・1 min read

Good morning, everyone.
Let's warm up for the start of the week by playing with strings.

s'yadoT egnellahc semoc from user xDranik on sraWedoC!

Write a function that takes in a string of one or more words and returns the same string, but with all words with five letters or more reversed. Strings passed in will consist of only letters and spaces.

Be sure to keep the order of the words the same and only reverse the letters.

Good luck, yppaH gniedoc!


Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge for a future post? Email yo+challenge@dev.to with your suggestions!

Posted on by:

thepracticaldev profile

dev.to staff

@thepracticaldev

The hardworking team behind dev.to ❤️

Discussion

markdown guide
 

JS quickie

theString => theString.split(' ').map(
  word => word.length >= 5 ?
    [...word].reverse().join('')
    : word
).join(' ')
 

Perl one-liner:

my $string = "Good luck, Happy coding!"; my $res = join " ", map { (length($_) >= 5) ? scalar reverse($_) : $_ } split / /, $string; say $res;

A bit more cleaned up, taking user input:

#!/usr/bin/perl

use v5.24;
use strict;
use warnings;
use Carp;

# Challenge says only letters and spaces, but this won't be picky
sub reverse_over_4 {
    my $string = shift or croak "Error: missing argument to function.";
    my $res    = join " ",
        map { ( length($_) > 4 ) ? scalar reverse($_) : $_ } split / /,
        $string;
    return $res;
}

print "Enter string: ";
my $input = <STDIN>;
chomp $input;
say reverse_over_4($input);

See runnable demo on PerlBanjo

 

Note that ($_) is not needed for length and reverse.

 

Yeah, true, that's just my personal preference 🙂

 

And one that modifies a string inplace using C

#include <stdio.h>
#include <string.h>

char *unspin(char *input) {
    for (
        char *eoinput = input + strlen(input),
             *nextSpace=input,
             *nextWord=input ;

        nextSpace < eoinput ;

        nextWord = nextSpace + 1 ,
        nextSpace = memchr(nextWord,' ',eoinput-nextWord),
        nextSpace = nextSpace ? nextSpace : eoinput
      ) {
        if (nextSpace >= nextWord + 5) for (
            char *wordStart = nextWord, 
            *wordEnd = nextSpace-1 ;

            wordStart < wordEnd ;

            ++wordStart,
            --wordEnd
        ) {
            *wordStart += *wordEnd;
            *wordEnd = *wordStart - *wordEnd;
            *wordStart -= *wordEnd;
        }
      }
      return input;
}

int main()
{
    char s[] = "Stop gninnipS My sdroW";
    printf("%s", unspin(s));

    return 0;
}
 

JavaScript

const spinner = sentence => sentence.split(' ')
                                    .reduce((acc, val) => `${acc} ${val.length > 4 ? val.split('').reverse().join('') : val}`, '')
                                    .trim();

Using reduce to generate a string from the array, but then I have to use trim as I end up with a space at either end :-/

Live demo on CodePen.

 

Nice one!
If you replace the trim() with a substring(1) then it'll work with strings that have spaces in front and behind since you're reliably adding a single space to the front, none to the back.

See the warning here re thesplit('') call. Seems the recommended way now is [...string].

 

Haskell, with user input

condRev :: [Char] -> [Char]
condRev s = if length s >= 5 then reverse s else s

toString :: [[Char]] -> [Char]
toString [] = []
toString (x:[]) = x
toString (x:xs) = x ++ " " ++ toString xs

solve :: [Char] -> IO ()
solve s = putStrLn $ toString $ map condRev $ words s

main :: IO ()
main = do
    line <- getLine
    solve line

 

Here is my Rust solution and test cases!

The Rust std lib made this one pretty simple!

pub fn spin_words(input: &str) -> String {
    input
        .split(" ")
        .map(|word| {
            if word.len() >= 5 {
                word.chars().rev().collect()
            } else {
                word.to_string()
            }
        })
        .collect::<Vec<String>>()
        .join(" ")
}

#[cfg(test)]
mod tests {
    use crate::*;

    #[test]
    fn it_works_for_words_shorter_than_4_chars() {
        assert_eq!(spin_words("Test"), "Test".to_string());
        assert_eq!(spin_words("Te"), "Te".to_string());
        assert_eq!(spin_words("ant"), "ant".to_string());
        assert_eq!(
            spin_words("lots of tiny word"),
            "lots of tiny word".to_string()
        );
    }

    #[test]
    fn it_words_for_the_example() {
        assert_eq!(
            spin_words("Stop gninnipS My sdroW"),
            "Stop Spinning My Words".to_string()
        );
    }

    #[test]
    fn it_words_with_space_padded_strings() {
        assert_eq!(
            spin_words(" Stop gninnipS My sdroW "),
            " Stop Spinning My Words ".to_string()
        );
    }
}
 

Python one-liner:
for x in input().split(): print(x[::1]*(len(x)<5) or x[::-1], end=' ')

65 chars at most :)

Or,
print(*map(lambda x: x[::1]*(len(x)<5)or x[::-1], input().split()))

Also 65 at most :)

 

This one was fun! I decided to approach it from a slightly different angle by building a general-purpose when function that could be used to decide when to map to a value (vs return the original value) based on a condition... just for kicks :-)

Here's my go:

const when = (when = () => false, fn = i => i) => 
    (v, idx, arr) => when(v, idx, arr) 
        ? (typeof fn === "function" ? fn(v, idx, arr) : fn)
        : v;

const reverseString = str => Array.from(str).reverse().join("");

const reverseWords = ({inString = "", ofLength = 0} = {}) => 
    inString.split(" ")
        .map(when(w => w.length >= ofLength, reverseString))
        .join(" ");

const gnirtS = reverseWords({inString: "one two three four", ofLength: 5});

Gist (w/ some tests): gist.github.com/kerrishotts/ac0f30...

 

A quick one in C#

 var input = Console.ReadLine().Split(' ');
 var output = new List<string>();
 foreach (var word in input)
 {
    if (word.Length <= 4)
    {
      output.Add(word);
      continue;
    }

    // word reversal. This can be done using inbuilt functions as well
    var wordArray = word.ToCharArray();
    var current = string.Empty;
    for (int i = word.Length - 1; i >= 0 ; i--)
    {
       current += wordArray[i];
    }

    output.Add(current);
 }

 Console.WriteLine(string.Join(" ", output));
 Console.ReadKey();
 

Rust, sort of in place

fn reverse_in_place(input: &mut [u8]) {
    input
        .split_mut(|&byte| byte == b' ')
        .filter(|word| word.len() >= 5)
        .for_each(<[_]>::reverse);
}

The rest of the story below the fold:

fn reverse(input: &str) -> String {
    let mut output = input.as_bytes().to_vec();
    reverse_in_place(&mut output);
    String::from_utf8(output).unwrap()
}

fn main() {
    let test = "Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!";
    println!("{}", reverse(test));
    println!("{}", reverse(&reverse(test)));
}

 

PHP 5.3.0 to 7.3.x:

preg_replace_callback('/(\b\w{5,}\b)+/i', function ($match) {
    return strrev($match[0]);
}, 'Hello world, this is a test string.');

PHP >= 7.4:

preg_replace_callback('/(\b\w{5,}\b)+/i', fn($match) => strrev($match[0]), 'Hello world, this is a test string.');
 

Elixir:

defmodule Spinner do
  @spec spin(String.t()) :: String.t()
  def spin(sentence) do
    String.replace(sentence, ~r/\w+/, fn word ->
      if String.length(word) > 4 do
        String.reverse(word)
      else
        word
      end
    end)
  end
end

"Please spin me around!"
|> Spinner.spin()
|> IO.puts()

# => "esaelP spin me dnuora!"
 

Ruby

def wordSpinner(string)
  string.split(" ").map do |word|
    if word.length >= 5
      word.reverse
    else
      word
    end
  end.join(" ")
end

Tests

wordSpinner("Everything small is just a smaller version of something big")
wordSpinner("Bad biscuits make the baker broke bro")
wordSpinner("Sometimes life is scary and dark")

Results

"gnihtyrevE llams is just a rellams noisrev of gnihtemos big"
"Bad stiucsib make the rekab ekorb bro" 
"semitemoS life is yracs and dark" 
 

Ruby

string.split.map{ |s| s.length >= 5 ? s.reverse! : s } * " "
 

My solution in js

const reverseWords = sentence => sentence.split(' ')
                                         .map(word => word.length > 4 ? word.split('').reverse().join('') : word)
                                         .join(' ');
 
const spinner = sentence =>
  sentence.split(' ')
    .map(word =>
      word.length > 4 ?
        word.split('').reverse().join('') :
        word
      )
    .join(' ')
 

Haskell:

flipWords :: String -> String
flipWords = unwords . map (\w -> if length w > 4 then reverse w else w) . words
 

JS/ES

const revWords = str => {
    return str
        .split(" ")
        .map(word => (word.length >= 5 ? [...word].reverse().join("") : word))
        .join(" ")
};
 

Python 3 : :P

s='yadoT egnellahc semoc from user xDranik on sraWedoC!'
print(s[::-1])