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

amoradell's solution

to Word Count in the Delphi Pascal Track

Published at Aug 26 2018 · 0 comments
Instructions
Test suite
Solution

Note:

This exercise has changed since this solution was written.

Given a phrase, count the occurrences of each word in that phrase.

For example for the input "olly olly in come free"

olly: 2
in: 1
come: 1
free: 1

Testing

In order to run the tests for this track, you will need to install DUnitX. Please see the installation instructions for more information.

Loading Exercises into Delphi

If Delphi is properly installed, and *.dpr file types have been associated with Delphi, then double clicking the supplied *.dpr file will start Delphi and load the exercise/project. control + F9 is the keyboard shortcut to compile the project or pressing F9 will compile and run the project.

Alternatively you may opt to start Delphi and load your project via. the File drop down menu.

When Questions Come Up

We monitor the Pascal-Delphi support room on gitter.im to help you with any questions that might arise.

Submitting Exercises

Note that, when trying to submit an exercise, make sure the exercise file you're submitting is in the exercism/delphi/<exerciseName> directory.

For example, if you're submitting ubob.pas for the Bob exercise, the submit command would be something like exercism submit <path_to_exercism_dir>/delphi/bob/ubob.pas.

Source

This is a classic toy problem, but we were reminded of it by seeing it in the Go Tour.

Submitting Incomplete Solutions

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

uWordCountTests.pas

unit uWordCountTests;

interface
uses
  System.Generics.Collections, DUnitX.TestFramework;
  
const
  CanonicalVersion = '1.2.0';  

type

  [TestFixture]
  WordCountTests = class(TObject)
  private
    procedure CompareDictionaries(Expected, Actual: TDictionary<String, integer>);
  public
    [Test]
    procedure Validate_CompareDictionaries;
	
    [Test]
    // [Ignore('Comment the "[Ignore]" statement to run the test')]
    procedure Count_one_word;

    [Test]
    [Ignore]
    procedure Count_one_of_each_word;

    [Test]
    [Ignore]
    procedure Multiple_occurrences_of_a_word;

    [Test]
    [Ignore]
    procedure Handles_cramped_lists;

    [Test]
    [Ignore]
    procedure Handles_expanded_lists;

    [Test]
    [Ignore]
    procedure Ignore_punctuation;

    [Test]
    [Ignore]
    procedure Include_numbers;

    [Test]
    [Ignore]
    procedure Normalize_case;

    [Test]
    [Ignore]
    procedure With_apostrophes;

    [Test]
    [Ignore]
    procedure With_quotations;

    [Test]
    [Ignore]
    procedure Multiple_spaces_not_detected_as_a_word;

  end;
	
implementation

uses SysUtils, uWordCount;

procedure WordCountTests.CompareDictionaries(Expected, Actual: TDictionary<String, Integer>);
var expectedPair: TPair<string, Integer>;
begin
  Assert.AreEqual(Expected.Count, Actual.Count);
  for expectedPair in Expected do
  begin
    Assert.IsTrue(Actual.ContainsKey(expectedPair.Key));
    Assert.AreEqual(expectedPair.Value, Actual[expectedPair.Key]);
  end;
end;

procedure WordCountTests.Validate_CompareDictionaries;
var expected, actual: TDictionary<String, integer>;
begin
  expected := TDictionary<String, integer>.Create;
  expected.Add('r',5);
  expected.Add('a',10);
  expected.Add('n',15);
  expected.Add('d',20);
  expected.Add('o',25);
  expected.Add('m',30);

  actual := TDictionary<String, Integer>.create(expected);

  CompareDictionaries(expected, actual);
end;

procedure WordCountTests.Count_one_word;
var expected, actual: TDictionary<String, integer>;
begin
  expected := TDictionary<String, integer>.Create;
  expected.Add('word',1);

  actual := WordCount('word').countWords;
  
  CompareDictionaries(expected, actual);
end;

procedure WordCountTests.Count_one_of_each_word;
var expected, actual: TDictionary<String, integer>;
begin
  expected := TDictionary<String, integer>.Create;
  expected.Add('one',1);
  expected.Add('of',1);
  expected.Add('each',1);

  actual :=  WordCount('one of each').countWords;
  
  CompareDictionaries(expected, actual);
end;

procedure WordCountTests.Multiple_occurrences_of_a_word;
var expected, actual: TDictionary<String, integer>;
begin
  expected := TDictionary<String, integer>.Create;
  expected.Add('one',1);
  expected.Add('fish',4);
  expected.Add('two',1);  
  expected.Add('red',1);
  expected.Add('blue',1);
  
  actual := WordCount('one fish two fish red fish blue fish').countWords;

  CompareDictionaries(expected, actual);
end;

procedure WordCountTests.Handles_cramped_lists;
var expected, actual: TDictionary<String, integer>;
begin
  expected := TDictionary<String, integer>.Create;
  expected.Add('one',1);
  expected.Add('two',1);
  expected.Add('three',1);  

  actual := WordCount('one,two,three').countWords;

  CompareDictionaries(expected, actual);  
end;

procedure WordCountTests.Handles_expanded_lists;
var expected, actual: TDictionary<String, integer>;
begin
  expected := TDictionary<String, integer>.Create;
  expected.Add('one',1);
  expected.Add('two',1);
  expected.Add('three',1);  

  actual := WordCount('one,\ntwo,\nthree').countWords;

  CompareDictionaries(expected, actual);  
end;

procedure WordCountTests.Ignore_punctuation;
var expected, actual: TDictionary<String, integer>;
begin
  expected := TDictionary<String, integer>.Create;
  expected.Add('car',1);
  expected.Add('carpet',1);
  expected.Add('as',1);  
  expected.Add('java',1);
  expected.Add('javascript',1);

  actual := WordCount('car: carpet as java: javascript!!&@$%^&').countWords;

  CompareDictionaries(expected, actual);  
end;

procedure WordCountTests.Include_numbers;
var expected, actual: TDictionary<String, integer>;
begin
  expected := TDictionary<String, integer>.Create;
  expected.Add('testing',2);
  expected.Add('1',1);
  expected.Add('2',1);  

  actual := WordCount('testing, 1, 2 testing').countWords;

  CompareDictionaries(expected, actual);
end;

procedure WordCountTests.Normalize_case;
var expected, actual: TDictionary<String, integer>;
begin
  expected := TDictionary<String, integer>.Create;
  expected.Add('go',3);
  expected.Add('stop',2);

  actual := WordCount('go Go GO Stop stop').countWords;

  CompareDictionaries(expected, actual);  
end;

procedure WordCountTests.With_apostrophes;
var expected, actual: TDictionary<String, integer>;
begin
  expected := TDictionary<String, integer>.Create;
  expected.Add('first',1);
  expected.Add('don''t',2);
  expected.Add('laugh',1);  
  expected.Add('then',1);
  expected.Add('cry',1);

  actual := WordCount('First: don''t laugh. Then: don''t cry.').countWords;

  CompareDictionaries(expected, actual);    
end;

procedure WordCountTests.With_quotations;
var expected, actual: TDictionary<String, integer>;
begin
  expected := TDictionary<String, integer>.Create;
  expected.Add('joe',1);
  expected.Add('can''t',1);
  expected.Add('tell',1);  
  expected.Add('between',1);
  expected.Add('large',2);
  expected.Add('and',1);  

  actual := WordCount('Joe can''t tell between ''large'' and large').countWords;

  CompareDictionaries(expected, actual);    
end;

procedure WordCountTests.Multiple_spaces_not_detected_as_a_word;
var expected, actual: TDictionary<String, integer>;
begin
  expected := TDictionary<String, integer>.Create;
  expected.Add('multiple',1);
  expected.Add('whitespaces',1);

  actual := WordCount(' multiple   whitespaces').countWords;

  CompareDictionaries(expected, actual);    
end;

initialization
  TDUnitX.RegisterTestFixture(WordCountTests);
end.
unit uWordCount;

interface
uses
  System.Generics.Collections;

type
  IWordCount = interface
    ['{955DF34E-254D-44E1-B4F3-91F0DCF8B764}']
    function countWords: TDictionary<string, integer>;
  end;

function WordCount(aWords: string): IWordCount;

implementation

uses SysUtils, RegularExpressions;

type
  TWordCount = class(TInterfacedObject, IWordCount)
  private
    FWords: string;
    FWordDict: TDictionary<string, integer>;
  public
    constructor Create(aWords: string);
    function countWords: TDictionary<string, integer>;
  end;

function WordCount(aWords: string): IWordCount;
begin
  result := TWordCount.Create(aWords);
end;

constructor TWordCount.Create(aWords: string);
begin
  inherited Create;
  FWords := aWords;
  FWordDict := TDictionary<string, integer>.Create;
end;

function TWordCount.countWords: TDictionary<string, integer>;
var
  aWords: TArray<string>;
  aWord: string;
begin
  // convert to lowercase and trim spaces
  FWords := Trim(Lowercase(FWords));
  // replace all separators with spaces
  FWords := TRegEx.Replace(FWords, '(,|\\n|'' | ''|:)', ' ', [roNone]);
  // get rid of extra characters
  FWords := TRegEx.Replace(FWords, '[^0-9a-z '']', '', [roNone]);
  // delete extra spaces
  FWords := TRegEx.Replace(FWords, '\s+', ' ', [roNone]);
  // splitting on spaces
  aWords := FWords.Split([' ']);

  for aWord in aWords do
  begin
    if (not FWordDict.ContainsKey(aWord)) then
      FWordDict.Add(aWord, 0);
    FWordDict.Items[aWord] := FWordDict.Items[aWord] + 1;
  end;
  result := FWordDict;
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?