Skip to content

Commit

Permalink
Merge pull request #216 from macta/etl-exercise
Browse files Browse the repository at this point in the history
Etl exercise
  • Loading branch information
macta authored Mar 22, 2019
2 parents 0cf907d + 8b6a613 commit 48f6634
Show file tree
Hide file tree
Showing 10 changed files with 310 additions and 15 deletions.
13 changes: 13 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,19 @@
"math"
]
},
{
"slug" : "etl",
"uuid" : "d6303b3f-0f41-0d00-a5e1-db510c056502",
"core" : false,
"auto_approve" : false,
"unlocked_by" : "reverse-string",
"difficulty" : 1,
"topics" : [
"maps",
"iteration",
"transforming"
]
},
{
"slug" : "hamming",
"uuid" : "69f615da-2b3f-0d00-a2ed-aca409f0590c",
Expand Down
21 changes: 21 additions & 0 deletions dev/src/Exercism/Etl.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"
I invert the storage of score/letter storage. While efficiency is not always the most crucial aspect of a soution, iterating over the dictionary associations (v.s. just value or just the keys) makes this solution quite trivial.
"
Class {
#name : #Etl,
#superclass : #Object,
#category : #'Exercism-Etl'
}

{ #category : #exercism }
Etl >> transformData: tileDictionary [
"Iterate over the input score/letter association to avoid an extra dictionary lookup"

^ tileDictionary associations
inject: Dictionary new
into: [ :result :scoreLettersPair |
scoreLettersPair value
do: [ :letter |
result at: letter asLowercase put: scoreLettersPair key asNumber ].
result ]
]
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ game while being scored at 4 in the Hawaiian-language version.
## Hint
TBD
While readability is preferred over efficiency, there is a solution that avoids multiple input looksups when solving this.
"
Class {
Expand All @@ -58,19 +58,31 @@ Class {
#instVars : [
'etlCalculator'
],
#category : #'ExercismWIP-Etl'
#category : #'Exercism-Etl'
}

{ #category : #config }
EtlTest class >> exercise [
"Answer the configured exercise meta data for this exercise, an ExercismExercise"

^(self createExerciseAfter: ReverseStringTest)
isCore: false;
difficulty: 1;
topics: #('maps' 'iteration' 'transforming');
yourself

]

{ #category : #config }
EtlTest class >> uuid [
"Answer a unique id for this exercise"
^'99fcc0bb-8040-0d00-8102-934201deb008'
^'d6303b3f-0f41-0d00-a5e1-db510c056502'
]

{ #category : #config }
EtlTest class >> version [
"Generated from specification: 15 March 2019"
^'1.0.0'
"Generated from specification: 22 March 2019"
^'1.0.1'
]

{ #category : #setup }
Expand All @@ -79,33 +91,33 @@ EtlTest >> setUp [
]

{ #category : #tests }
EtlTest >> test01_TransformsTheASetOfScrabbleDataPreviouslyIndexedByTheTileScoreToASetOfDataIndexedByTheTileLetterASingleLetter [
EtlTest >> test01_TransformsASingleLetter [
| result |

result := etlCalculator transform1: #('A' ) .
result := etlCalculator transformData: ((Dictionary new) add: ('1'->#('A' )); yourself) .
self assert: result equals: ((Dictionary new) add: ('a'->1); yourself)
]

{ #category : #tests }
EtlTest >> test02_TransformsTheASetOfScrabbleDataPreviouslyIndexedByTheTileScoreToASetOfDataIndexedByTheTileLetterSingleScoreWithMultipleLetters [
EtlTest >> test02_TransformASingleScoreWithMultipleLetters [
| result |

result := etlCalculator transform1: #('A' 'E' 'I' 'O' 'U' ) .
result := etlCalculator transformData: ((Dictionary new) add: ('1'->#('A' 'E' 'I' 'O' 'U' )); yourself) .
self assert: result equals: ((Dictionary new) add: ('i'->1); add: ('e'->1); add: ('a'->1); add: ('u'->1); add: ('o'->1); yourself)
]

{ #category : #tests }
EtlTest >> test03_TransformsTheASetOfScrabbleDataPreviouslyIndexedByTheTileScoreToASetOfDataIndexedByTheTileLetterMultipleScoresWithMultipleLetters [
EtlTest >> test03_TransformMultipleScoresWithMultipleLetters [
| result |

result := etlCalculator transform1: #('A' 'E' ) at2: #('D' 'G' ) .
result := etlCalculator transformData: ((Dictionary new) add: ('1'->#('A' 'E' )); add: ('2'->#('D' 'G' )); yourself) .
self assert: result equals: ((Dictionary new) add: ('g'->2); add: ('a'->1); add: ('d'->2); add: ('e'->1); yourself)
]

{ #category : #tests }
EtlTest >> test04_TransformsTheASetOfScrabbleDataPreviouslyIndexedByTheTileScoreToASetOfDataIndexedByTheTileLetterMultipleScoresWithDifferingNumbersOfLetters [
EtlTest >> test04_TransformsMultipleScoresWithDifferingNumbersOfLetters [
| result |

result := etlCalculator transform8: #('J' 'X' ) at4: #('F' 'H' 'V' 'W' 'Y' ) at2: #('D' 'G' ) at5: #('K' ) at3: #('B' 'C' 'M' 'P' ) at1: #('A' 'E' 'I' 'O' 'U' 'L' 'N' 'R' 'S' 'T' ) at10: #('Q' 'Z' ) .
result := etlCalculator transformData: ((Dictionary new) add: ('8'->#('J' 'X' )); add: ('4'->#('F' 'H' 'V' 'W' 'Y' )); add: ('2'->#('D' 'G' )); add: ('5'->#('K' )); add: ('3'->#('B' 'C' 'M' 'P' )); add: ('1'->#('A' 'E' 'I' 'O' 'U' 'L' 'N' 'R' 'S' 'T' )); add: ('10'->#('Q' 'Z' )); yourself) .
self assert: result equals: ((Dictionary new) add: ('g'->2); add: ('z'->10); add: ('s'->1); add: ('l'->1); add: ('e'->1); add: ('x'->8); add: ('q'->10); add: ('j'->8); add: ('c'->3); add: ('v'->4); add: ('o'->1); add: ('h'->4); add: ('a'->1); add: ('t'->1); add: ('m'->3); add: ('f'->4); add: ('y'->4); add: ('r'->1); add: ('k'->5); add: ('d'->2); add: ('w'->4); add: ('p'->3); add: ('i'->1); add: ('b'->3); add: ('u'->1); add: ('n'->1); yourself)
]
11 changes: 9 additions & 2 deletions dev/src/ExercismDev/ClyExercismGenerateCommand.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ Class {
#category : #'ExercismDev-Menus'
}

{ #category : #'world menu' }
ClyExercismGenerateCommand class >> contextMenuOrder [
<classAnnotationDependency>

^20
]

{ #category : #'world menu' }
ClyExercismGenerateCommand class >> worldMenuCommandOn: aBuilder [
<worldMenu>
Expand All @@ -19,8 +26,8 @@ ClyExercismGenerateCommand class >> worldMenuCommandOn: aBuilder [
order: self contextMenuOrder;
action: [ templateCommand execute ];
"iconName: templateCommand defaultMenuIconName;"
help: templateCommand description.
"withSeparatorAfter "
help: templateCommand description;
withSeparatorAfter
]

{ #category : #testing }
Expand Down
7 changes: 7 additions & 0 deletions dev/src/ExercismDev/ClyExercismVisualizeCommand.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ Class {
#category : #'ExercismDev-Menus'
}

{ #category : #'world menu' }
ClyExercismVisualizeCommand class >> contextMenuOrder [
<classAnnotationDependency>

^15
]

{ #category : #'world menu' }
ClyExercismVisualizeCommand class >> worldMenuCommandOn: aBuilder [
<worldMenu>
Expand Down
13 changes: 13 additions & 0 deletions dev/src/ExercismDev/ExercismExerciseGenerator.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ ExercismExerciseGenerator class >> generate [
path ifNotNil: [ self new generateFrom: (self defaultPath: path) ]
]

{ #category : #examples }
ExercismExerciseGenerator class >> worldMenuCommandOn: aBuilder [
<worldMenu>

(aBuilder item: 'Generate exercises')
parent: #Exercism;
order: 150.0;
action: [ self generate ];
"iconName: templateCommand defaultMenuIconName;"
help: 'Re-Generate exercises from the canonical problem-description'.
"withSeparatorBefore"
]

{ #category : #internal }
ExercismExerciseGenerator >> compile: src for: aClass selector: aSelector protocol: aName [
| cm aContext |
Expand Down
1 change: 1 addition & 0 deletions exercises/etl/.meta/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
While readability is preferred over efficiency, there is a solution that avoids multiple input looksups when solving this.
21 changes: 21 additions & 0 deletions exercises/etl/.meta/solution/Etl.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"
I invert the storage of score/letter storage. While efficiency is not always the most crucial aspect of a soution, iterating over the dictionary associations (v.s. just value or just the keys) makes this solution quite trivial.
"
Class {
#name : #Etl,
#superclass : #Object,
#category : #'Exercism-Etl'
}

{ #category : #exercism }
Etl >> transformData: tileDictionary [
"Iterate over the input score/letter association to avoid an extra dictionary lookup"

^ tileDictionary associations
inject: Dictionary new
into: [ :result :scoreLettersPair |
scoreLettersPair value
do: [ :letter |
result at: letter asLowercase put: scoreLettersPair key asNumber ].
result ]
]
123 changes: 123 additions & 0 deletions exercises/etl/EtlTest.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"
# Etl
We are going to do the `Transform` step of an Extract-Transform-Load.
### ETL
Extract-Transform-Load (ETL) is a fancy way of saying, ""We have some crufty, legacy data over in this system, and now we need it in this shiny new system over here, so
we're going to migrate this.""
(Typically, this is followed by, ""We're only going to need to run this
once."" That's then typically followed by much forehead slapping and
moaning about how stupid we could possibly be.)
### The goal
We're going to extract some scrabble scores from a legacy system.
The old system stored a list of letters per score:
- 1 point: ""A"", ""E"", ""I"", ""O"", ""U"", ""L"", ""N"", ""R"", ""S"", ""T"",
- 2 points: ""D"", ""G"",
- 3 points: ""B"", ""C"", ""M"", ""P"",
- 4 points: ""F"", ""H"", ""V"", ""W"", ""Y"",
- 5 points: ""K"",
- 8 points: ""J"", ""X"",
- 10 points: ""Q"", ""Z"",
The shiny new scrabble system instead stores the score per letter, which
makes it much faster and easier to calculate the score for a word. It
also stores the letters in lower-case regardless of the case of the
input letters:
- ""a"" is worth 1 point.
- ""b"" is worth 3 points.
- ""c"" is worth 3 points.
- ""d"" is worth 2 points.
- Etc.
Your mission, should you choose to accept it, is to transform the legacy data
format to the shiny new format.
### Notes
A final note about scoring, Scrabble is played around the world in a
variety of languages, each with its own unique scoring table. For
example, an ""E"" is scored at 2 in the Māori-language version of the
game while being scored at 4 in the Hawaiian-language version.
## Hint
While readability is preferred over efficiency, there is a solution that avoids multiple input looksups when solving this.
"
Class {
#name : #EtlTest,
#superclass : #ExercismTest,
#instVars : [
'etlCalculator'
],
#category : #'Exercism-Etl'
}

{ #category : #config }
EtlTest class >> exercise [
"Answer the configured exercise meta data for this exercise, an ExercismExercise"

^(self createExerciseAfter: ReverseStringTest)
isCore: false;
difficulty: 1;
topics: #('maps' 'iteration' 'transforming');
yourself

]

{ #category : #config }
EtlTest class >> uuid [
"Answer a unique id for this exercise"
^'d6303b3f-0f41-0d00-a5e1-db510c056502'
]

{ #category : #config }
EtlTest class >> version [
"Generated from specification: 22 March 2019"
^'1.0.1'
]

{ #category : #setup }
EtlTest >> setUp [
etlCalculator := Etl new
]

{ #category : #tests }
EtlTest >> test01_TransformsASingleLetter [
| result |

result := etlCalculator transformData: ((Dictionary new) add: ('1'->#('A' )); yourself) .
self assert: result equals: ((Dictionary new) add: ('a'->1); yourself)
]

{ #category : #tests }
EtlTest >> test02_TransformASingleScoreWithMultipleLetters [
| result |

result := etlCalculator transformData: ((Dictionary new) add: ('1'->#('A' 'E' 'I' 'O' 'U' )); yourself) .
self assert: result equals: ((Dictionary new) add: ('i'->1); add: ('e'->1); add: ('a'->1); add: ('u'->1); add: ('o'->1); yourself)
]

{ #category : #tests }
EtlTest >> test03_TransformMultipleScoresWithMultipleLetters [
| result |

result := etlCalculator transformData: ((Dictionary new) add: ('1'->#('A' 'E' )); add: ('2'->#('D' 'G' )); yourself) .
self assert: result equals: ((Dictionary new) add: ('g'->2); add: ('a'->1); add: ('d'->2); add: ('e'->1); yourself)
]

{ #category : #tests }
EtlTest >> test04_TransformsMultipleScoresWithDifferingNumbersOfLetters [
| result |

result := etlCalculator transformData: ((Dictionary new) add: ('8'->#('J' 'X' )); add: ('4'->#('F' 'H' 'V' 'W' 'Y' )); add: ('2'->#('D' 'G' )); add: ('5'->#('K' )); add: ('3'->#('B' 'C' 'M' 'P' )); add: ('1'->#('A' 'E' 'I' 'O' 'U' 'L' 'N' 'R' 'S' 'T' )); add: ('10'->#('Q' 'Z' )); yourself) .
self assert: result equals: ((Dictionary new) add: ('g'->2); add: ('z'->10); add: ('s'->1); add: ('l'->1); add: ('e'->1); add: ('x'->8); add: ('q'->10); add: ('j'->8); add: ('c'->3); add: ('v'->4); add: ('o'->1); add: ('h'->4); add: ('a'->1); add: ('t'->1); add: ('m'->3); add: ('f'->4); add: ('y'->4); add: ('r'->1); add: ('k'->5); add: ('d'->2); add: ('w'->4); add: ('p'->3); add: ('i'->1); add: ('b'->3); add: ('u'->1); add: ('n'->1); yourself)
]
Loading

0 comments on commit 48f6634

Please sign in to comment.