🎉 Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io 🎉
Avatar of alexwebgr

alexwebgr's solution

to Isogram in the Ruby Track

Published at Jan 15 2021 · 0 comments
Instructions
Test suite
Solution

Determine if a word or phrase is an isogram.

An isogram (also known as a "nonpattern word") is a word or phrase without a repeating letter, however spaces and hyphens are allowed to appear multiple times.

Examples of isograms:

  • lumberjacks
  • background
  • downstream
  • six-year-old

The word isograms, however, is not an isogram, because the s repeats.


For installation and learning resources, refer to the Ruby resources page.

For running the tests provided, you will need the Minitest gem. Open a terminal window and run the following command to install minitest:

gem install minitest

If you would like color output, you can require 'minitest/pride' in the test file, or note the alternative instruction, below, for running the test file.

Run the tests from the exercise directory using the following command:

ruby isogram_test.rb

To include color from the command line:

ruby -r minitest/pride isogram_test.rb

Source

Wikipedia https://en.wikipedia.org/wiki/Isogram

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

isogram_test.rb

require 'minitest/autorun'
require_relative 'isogram'

# Common test data version: 1.7.0 74869e8
class IsogramTest < Minitest::Test
  def test_empty_string
    # skip
    input = ""
    assert Isogram.isogram?(input), "Expected true, '#{input}' is an isogram"
  end

  def test_isogram_with_only_lower_case_characters
    skip
    input = "isogram"
    assert Isogram.isogram?(input), "Expected true, '#{input}' is an isogram"
  end

  def test_word_with_one_duplicated_character
    skip
    input = "eleven"
    refute Isogram.isogram?(input), "Expected false, '#{input}' is not an isogram"
  end

  def test_word_with_one_duplicated_character_from_the_end_of_the_alphabet
    skip
    input = "zzyzx"
    refute Isogram.isogram?(input), "Expected false, '#{input}' is not an isogram"
  end

  def test_longest_reported_english_isogram
    skip
    input = "subdermatoglyphic"
    assert Isogram.isogram?(input), "Expected true, '#{input}' is an isogram"
  end

  def test_word_with_duplicated_character_in_mixed_case
    skip
    input = "Alphabet"
    refute Isogram.isogram?(input), "Expected false, '#{input}' is not an isogram"
  end

  def test_word_with_duplicated_character_in_mixed_case_lowercase_first
    skip
    input = "alphAbet"
    refute Isogram.isogram?(input), "Expected false, '#{input}' is not an isogram"
  end

  def test_hypothetical_isogrammic_word_with_hyphen
    skip
    input = "thumbscrew-japingly"
    assert Isogram.isogram?(input), "Expected true, '#{input}' is an isogram"
  end

  def test_hypothetical_word_with_duplicated_character_following_hyphen
    skip
    input = "thumbscrew-jappingly"
    refute Isogram.isogram?(input), "Expected false, '#{input}' is not an isogram"
  end

  def test_isogram_with_duplicated_hyphen
    skip
    input = "six-year-old"
    assert Isogram.isogram?(input), "Expected true, '#{input}' is an isogram"
  end

  def test_made_up_name_that_is_an_isogram
    skip
    input = "Emily Jung Schwartzkopf"
    assert Isogram.isogram?(input), "Expected true, '#{input}' is an isogram"
  end

  def test_duplicated_character_in_the_middle
    skip
    input = "accentor"
    refute Isogram.isogram?(input), "Expected false, '#{input}' is not an isogram"
  end

  def test_same_first_and_last_characters
    skip
    input = "angola"
    refute Isogram.isogram?(input), "Expected false, '#{input}' is not an isogram"
  end
end
# Isogram
# Determine if a word or phrase is an isogram.
# An isogram (also known as a "nonpattern word") is a word or phrase without a repeating letter, however
# spaces and hyphens are allowed to appear multiple times.

class Isogram
  IGNORED = ["-", " "]

  def self.isogram?(word)
    new(word).isogram?
  end

  private

  attr_reader :word
  attr_reader :letters
  attr_reader :ignored

  def initialize(word, ignored = IGNORED)
    @word = word.downcase
    @ignored = ignored
  end

  def letters
    @letters ||= ignored.empty? ?
                   word.chars :
                   word.scan(/[^#{Regexp.quote(ignored.join)}]/)
                 .tally
  end

  def nth_order_isogram
    @nth_order_isogram ||= letters.values.uniq
  end

  def ordinalize(num)
    case num % 10
    when 1
      return "#{num}st"
    when 2
      return "#{num}nd"
    when 3
      return "#{num}rd"
    else
      return "#{num}th"
    end
  end
  public

  def isogram?(order = 0)
    nth_order_isogram.size <= 1 && order == nth_order_isogram.first ||
      nth_order_isogram.size <= 1
  end

  def to_s
    if nth_order_isogram.size <= 1
      "You gave \"#{word}\" and it is a #{ordinalize(nth_order_isogram.first)} order isogram."
    else
      "You gave \"#{word}\" and it is not an isogram."
    end
  end
end

if defined? Minitest
  describe 'Reporting' do
    describe 'A report method' do
      describe 'when not an isogram' do
        it 'must give details' do
          actual = Isogram.new('bananas').to_s
          expected = 'You gave "bananas" and it is not an isogram.'
          _(actual).must_equal expected
        end
      end

      describe 'when it is an isogram' do
        it 'must give details 1st level' do
          actual = Isogram.new('isogram').to_s
          expected = 'You gave "isogram" and it is a 1st order isogram.'
          _(actual).must_equal expected
        end

        it 'must give details 2nd level' do
          actual = Isogram.new('deed').to_s
          expected = 'You gave "deed" and it is a 2nd order isogram.'
          _(actual).must_equal expected
        end
      end
    end
  end
end

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?