DEV Community

dev.to staff
dev.to staff

Posted on

Daily Challenge #2 - String Diamond

Welcome to Day 2 of our challenge series. Today, you’ll be using some clean and polished code to create a clean and polished diamond.

Our challenge comes from user @jayeshcp on CodeWars.

Your task is to return a string that displays a diamond shape on the screen using asterisk (“*”) characters.

The shape that the print method will return should resemble a diamond. A number provided as input will represent the number of asterisks printed on the middle line. The line above and below will be centered and will have two less asterisks than the middle line. This reduction will continue for each line until a line with a single asterisk is printed at the top and bottom of the figure.

Return null if input is an even number or a negative number.

Note: JS and Python students must implement diamond() method and return None (Py) or null(JS) for invalid input.

Bonus points awarded for experimenting with any extra features.

Good luck!


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!

Latest comments (85)

Collapse
 
khaloscar profile image
khaloscar

Implemented this in rust with some tests. I am still learning and got a lot of experimentation done. Idk how much it matters in this case but I tried pre-allocating capacities for any vectors or strings.

At first I used modulo to get uneven numbers in the for loop but I got stuck doing it.
Fond a solutuion here in the comments of a guy using abs-values, that inspired me to instead only create the diamond up to its mid-row and then reflect it around that row.

Could maybe improve and use iterators instead of for-loop, idk?
with.capacity(), seems to take usize only, some improvement could be done here, for example I convert to usize for each capacity call.
Adding the padding at the end of a line isnt really neccessary?

fn diamond(length: i32) -> Option<String> {

    // Return early with None if len less than 1
    if length < 1 {
        return  None;
    }

    // Find mid-row/mid-point 
    // and pre-alloc capacity for output-vector
    // note capacity seems to determine amnt of elements
    // not capacity to hold data(?)
    let midpoint = (length + 1)/2;
    let mut output = Vec::with_capacity(length as usize);

    // Create each line up to and including midrow, padding decreases
    // => asterixes increases
    for i in 1..=midpoint {
        let mut line = String::with_capacity(length as usize);
        let pad = midpoint - i;
        let padding = concat_char_cap(' ', pad);
        let asterix = concat_char_cap('*', length-pad*2);

        // push the strings together to form the line
        line.push_str(&padding);
        line.push_str(&asterix);
        line.push_str(&padding);
        line.push_str("\n");

        // push into output_vector
        output.push(line);
    }
    //reflect half of the vector around its center row
    // maybe this could save some computation for
    // larger diamonds
    let mut beg = output[0..output.len()-1].to_vec();
    beg.reverse();
    output.append(&mut beg);

    // create iterator and collect into String.
    // wrap around Some
    Some(output.into_iter().collect())

}

fn concat_char_cap(c: char, n: i32) -> String {
    // pre-alloc string with capacity, 
    // maybe its more efficient for larger rows
    let n = n as usize;
    let mut output = String::with_capacity(n);

    if n == 1 {
        output.push(c);
    } else if n > 1 {
        for _i in 0..=n-1 {
            output.push(c);
        }      
    }

    output
}

fn main() {

    println!("{:?}", diamond(0));

}



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

    #[test]
    fn case_successful_dimaond() {
        let midrow = 5;
        let expected = "  \
  *  
 *** 
*****
 *** 
  *  \n".to_string();

    assert_eq!(diamond(midrow), Some(expected));
    }

    #[test]
    fn case_empty_diamond() {
        let midrow = 0;

        assert_eq!(diamond(midrow), None);
    }

    #[test]
    fn case_negative_diamond() {
        assert_eq!(diamond(-2), None)
    }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
propthink profile image
propthink

No one will see this but I spent a couple of hours trying to figure it out:

#include <iostream>

// value dictates total number of asterisks in middle row
bool print( int value )
{
    // if user value is even, or equal to zero
    if( ( value % 2 ) == 0 || value == 0 )
    {
        return false;
    }
    // iterate through each possible ascending row
    for( int i { 0 }; i <= value; ++i )
    {
        // only print new row on odd-numbered values
        if( ( i % 2 ) != 0 )
        {
            // print buffer for alignment
            for( int j { 0 }; j < ( ( value - i ) / 2 ); ++j )
            {
                std::cout << " ";
            }
            // print this row
            for( int k { 0 }; k < i; ++k )
            {
                std::cout << "*";
            }
            std::cout << '\n';
        }
    }
    // iterate through each possible descending row
    // avoid printing the middle row twice
    for( int i { value - 1 }; i > 0; --i )
    {
        // only print new row on odd-numbered values
        if( ( i % 2 ) != 0 )
        {
            // print buffer for alignment
            for( int j { 0 }; j < ( ( value - i ) / 2 ); ++j )
            {
                std::cout << " ";
            }
            // print this row
            for( int k { 0 }; k < i; ++k )
            {
                std::cout << "*";
            }
            std::cout << '\n';
        }
    }
    return true;
}

int main()
{
    if( !print( 11 ) )
    {
        std::cout << "invalid input detected!" << '\n';
    }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
riadulislam008 profile image
riadulIslam008 • Edited

Javascript

Collapse
 
riadulislam008 profile image
riadulIslam008

Javascript ans.

Collapse
 
nishikr profile image
Nishanth Kr

Couldnt find java one here. so uploading mine (suggestions are welcome)

if (base % 2 != 0 && base>0) {
int spaceDiff = (base / 2);
int star, row, order = 2;
// upper and middle
for (row = 0; row < base; row++) {
for (int space = spaceDiff; space > order - 2; space--) {
System.out.print(" ");
}
++order;
for (star = 0; star <= row; star++) {
System.out.print("");
}
System.out.println();
++row;
}
//bottom
order = spaceDiff;
for (row = base - 2; row > 0; row--) {
for (int space = order; space < spaceDiff + 1; space++) {
System.out.print(" ");
}
--order;
for (star = row; star > 0; star--) {
System.out.print("
");
}
System.out.println();
--row;
}
}

Collapse
 
jasman7799 profile image
Jarod Smith
print((lambda maxStars = int(input("Star Count: ")) : "Can't be a star =(" if not maxStars % 2 else "".join(map(lambda i: "".join([" "]*(int((maxStars - ((i+1) * 2 - 1 if (i+1) * 2 - 1 <= maxStars else (maxStars-(i+1) + 1)*2 -1))/2)) + ["*"]*((i+1) * 2 - 1 if (i+1) * 2 - 1 <= maxStars else (maxStars-(i+1) + 1)*2 -1)) +"\n", range(maxStars))))())

one line functional python, why I dunno.

Collapse
 
dimitrilahaye profile image
Dimitri Lahaye

Sorry for that (JS)

function diamond(int) {
  if (int < 0 || !(int % 2)) {
    return null;
  }
  write = (i,j) => ' '.repeat(j) + '*'.repeat(i) + '\n';
  let ret = '';
  for (let i = 1, j = Math.floor(int / 2); j >= 0; j--, i += 2) {
    ret += write(i,j);
  }
  for (let i = int - 2, j = 1; j < int && i > 0; j++, i -= 2) {
    ret += write(i,j);
  }
  return ret;
}
Collapse
 
peter279k profile image
peter279k

Here is my simple diamond solution with Python:

def diamond(n):
    # Make some diamonds!
    if n % 2 == 0 or n <= 0:
        return None
    if n == 1:
        return "*\n"

    array = list(range(1, n+1))
    result = ""
    white_space_count = list(range(1, int((n - (n % 2)) / 2)+1))
    white_space_count.reverse()
    white_space_index = 0
    for count in array:
        if count % 2 == 0:
            continue
        if white_space_index != len(white_space_count):
            result += (" " * white_space_count[white_space_index])
            white_space_index += 1
        result += ("*" * count) + "\n"

    array.reverse()
    array = array[1:]
    white_space_count.reverse()
    white_space_index = 0

    for count in array:
        if count % 2 == 0:
            continue
        if white_space_index != len(white_space_count):
            result += (" " * white_space_count[white_space_index])
            white_space_index += 1
        result += ("*" * count) + "\n"

    return result
Collapse
 
lucasromerodb profile image
Luke • Edited

JS

const drawRow = (qty, replaceWith) => {
  let str = "";
  for (let i = 0; i < qty; i++) {
    str += replaceWith;
  }

  return str;
};

const printDiamond = (n) => {
  if (n % 2 === 0 || n < 0) {
    return "The number should be odd and positive";
  }

  let row = "";
  let stars = 0;

  for (let i = 0; i < n; i++) {
    if (i > parseInt(n / 2)) {
      stars -= 2;
    } else {
      stars = i * 2 + 1;
    }

    const spaces = (n - stars) / 2;

    row += drawRow(spaces, " ");
    row += drawRow(stars, "*");
    row += drawRow(spaces, " ");

    if (i !== n - 1) {
      row += "\n";
    }
  }

  return row;
};

console.log(printDiamond(11));
Collapse
 
figueroadavid profile image
David Figueroa

Powershell

It will automatically center to the specified line width, and requires that the line be at least as wide as the diamond itself.

function show-diamond {
    param(
        [ValidateScript( {$_ -ge 11} )]
        [int]$LineWidth = 11
    )

    $range = [System.Collections.Generic.List[int]]::new()
    (1..11).Where{$_ % 2 -gt 0} | ForEach-Object { $range.Add($_) }
    (9..1).Where{$_ % 2 -gt 0} | ForEach-Object { $range.Add($_) }

    $output = [System.Collections.Generic.List[string]]::new()
    for ($i = 0; $i -lt $range.Count; $i++) {
        $MidPoint = [math]::Round(($LineWidth - $range[$i]) / 2, [System.MidpointRounding]::AwayFromZero)
        $String = '{0}{1}' -f (' ' * $MidPoint), ('*' * $range[$i])
        $output.Add($string)
    }
    $output
}

David F.

Collapse
 
centanomics profile image
Cent

Just used some for loops to print all of the asterisks, after doing a check for over 0 and odd, and added some css to make the shape of a diamond.

Codepen


const stringDiamond = rows => {
  if (rows > 0 && rows % 2 === 1) {
    let diamond = "";
    for(let i = 1; i< rows + 1; i += 2) {
      diamond += "<p>";
      for(let j = 1; j < i + 1; j++) {
        diamond += "*";
      }
      diamond += "</p>";
    }
    for(let i = rows - 2; i> 0; i-=2) {
      diamond += "<p>";
      for(let j = 1; j < i + 1; j++) {
        diamond += "*";
      }
      diamond += "</p>";
    }
    return diamond;
  } else {
    return "null"
  }
}

The CSS making the asterisks look like a diamond


#answer {
  text-align: center;
  line-height: 5px;
}

Collapse
 
neotamizhan profile image
Siddharth Venkatesan

Ruby

def diamond(middle)
  return nil if middle < 0 or middle.even?
  d = []
  (1..middle).step(2).each do |i|
    spaces = " " * ((middle - i).to_i/2)    
    d << "#{spaces}#{"*" * i}#{spaces}"
  end  
  d + d[0..-2].reverse  
end

puts diamond(7)
Collapse
 
tblanshard profile image
Tallie

My solution using Python :)

def diamond(mid):
    if (mid % 2 == 0) or (mid < 0):
        return None
    else:
        spaces = (mid - 1) // 2
        asterisks = 1
        while asterisks < (mid):
            print((" " * spaces) + ("*" * asterisks) + (" " * spaces))
            spaces -= 1
            asterisks += 2
        spaces = 0
        asterisks = mid
        while asterisks > 0:
            print((" " * spaces) + ("*" * asterisks) + (" " * spaces))
            spaces += 1
            asterisks -= 2
Collapse
 
mellamoadan profile image
Adan ϟ

Dart

import 'dart:io';

main(){
  stdout.writeln('Center Lenght?');
  int center = int.tryParse(stdin.readLineSync());
  if(center == null || center%2 == 0){
    print('Center can\'t be null or an even number');
  }

  for(var i = 1; i < center; i+=2){
    print((" " * ((center - i) ~/ 2 )) + ("*" * i));
  }
  for(var i = center; i >= 1; i-=2){
    print((" " * ((center - i) ~/ 2 )) + ("*" * i));
  }
}
Collapse
 
margo1993 profile image
margo1993

My go exercise two

func BuildAsteriskDiamond(weight int) (string, error) {
    if weight < 3 {
        return "", errors.New("Can't build diamond less than 3 asterisk")
    }

    if weight % 2 == 0 {
        return "", errors.New("Even number is not allowed!")
    }

    result := ""
    var height int = weight / 2
    var lineWidth int = height + 1
    asteriskCount := 1

    for i := 0; i < weight; i++ {
        spacesCount := abs(height - i)

        result = result + repeatCharacter(" ", spacesCount) + repeatCharacter("*", asteriskCount) + "\n"

        if i < height {
            asteriskCount += 2
            lineWidth++
        } else {
            asteriskCount -= 2
            lineWidth--
        }
    }

    return result, nil
}

func repeatCharacter(character string, count int) string {
    result := ""
    for i := 0; i < count; i++ {
        result += character
    }
    return result
}

func abs(x int) int {
    if x < 0 {
        return -x
    }
    return x
}

I didn't find any good solution to abs integer in go :(