From 5581bee1aa247413c9f8f1064faafe9c12d41d76 Mon Sep 17 00:00:00 2001 From: Tim Mackinnon Date: Fri, 22 Mar 2019 14:02:40 +0000 Subject: [PATCH 1/3] Etl exercise Adds the generate command to the world menu (so its a bit easier to generate the exercism meta data files) --- dev/src/Exercism/Etl.class.st | 21 ++++++++++ .../EtlTest.class.st | 38 ++++++++++++------- .../ClyExercismGenerateCommand.class.st | 11 +++++- .../ClyExercismVisualizeCommand.class.st | 7 ++++ .../ExercismExerciseGenerator.class.st | 13 +++++++ 5 files changed, 75 insertions(+), 15 deletions(-) create mode 100644 dev/src/Exercism/Etl.class.st rename dev/src/{ExercismWIP => Exercism}/EtlTest.class.st (67%) diff --git a/dev/src/Exercism/Etl.class.st b/dev/src/Exercism/Etl.class.st new file mode 100644 index 00000000..d2465a77 --- /dev/null +++ b/dev/src/Exercism/Etl.class.st @@ -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 ] +] diff --git a/dev/src/ExercismWIP/EtlTest.class.st b/dev/src/Exercism/EtlTest.class.st similarity index 67% rename from dev/src/ExercismWIP/EtlTest.class.st rename to dev/src/Exercism/EtlTest.class.st index 5943da44..72238732 100644 --- a/dev/src/ExercismWIP/EtlTest.class.st +++ b/dev/src/Exercism/EtlTest.class.st @@ -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 { @@ -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 } @@ -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) ] diff --git a/dev/src/ExercismDev/ClyExercismGenerateCommand.class.st b/dev/src/ExercismDev/ClyExercismGenerateCommand.class.st index cb2850ad..4483beba 100644 --- a/dev/src/ExercismDev/ClyExercismGenerateCommand.class.st +++ b/dev/src/ExercismDev/ClyExercismGenerateCommand.class.st @@ -7,6 +7,13 @@ Class { #category : #'ExercismDev-Menus' } +{ #category : #'world menu' } +ClyExercismGenerateCommand class >> contextMenuOrder [ + + + ^20 +] + { #category : #'world menu' } ClyExercismGenerateCommand class >> worldMenuCommandOn: aBuilder [ @@ -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 } diff --git a/dev/src/ExercismDev/ClyExercismVisualizeCommand.class.st b/dev/src/ExercismDev/ClyExercismVisualizeCommand.class.st index 82d2d55e..2fbed0df 100644 --- a/dev/src/ExercismDev/ClyExercismVisualizeCommand.class.st +++ b/dev/src/ExercismDev/ClyExercismVisualizeCommand.class.st @@ -7,6 +7,13 @@ Class { #category : #'ExercismDev-Menus' } +{ #category : #'world menu' } +ClyExercismVisualizeCommand class >> contextMenuOrder [ + + + ^15 +] + { #category : #'world menu' } ClyExercismVisualizeCommand class >> worldMenuCommandOn: aBuilder [ diff --git a/dev/src/ExercismDev/ExercismExerciseGenerator.class.st b/dev/src/ExercismDev/ExercismExerciseGenerator.class.st index 72eea6c6..44c2018c 100644 --- a/dev/src/ExercismDev/ExercismExerciseGenerator.class.st +++ b/dev/src/ExercismDev/ExercismExerciseGenerator.class.st @@ -41,6 +41,19 @@ ExercismExerciseGenerator class >> generate [ path ifNotNil: [ self new generateFrom: (self defaultPath: path) ] ] +{ #category : #examples } +ExercismExerciseGenerator class >> worldMenuCommandOn: aBuilder [ + + + (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 | From f2aa6c091c129a6c874d4189b4f9390fcfe685f9 Mon Sep 17 00:00:00 2001 From: Tim Mackinnon Date: Fri, 22 Mar 2019 14:11:31 +0000 Subject: [PATCH 2/3] update the exercise config and meta data --- config.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/config.json b/config.json index 74787a95..a487b839 100644 --- a/config.json +++ b/config.json @@ -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", From 8b6a613077bca858c47350fc69382679e85101be Mon Sep 17 00:00:00 2001 From: Tim Mackinnon Date: Fri, 22 Mar 2019 14:11:47 +0000 Subject: [PATCH 3/3] update the exercise config and meta data --- exercises/etl/.meta/hints.md | 1 + exercises/etl/.meta/solution/Etl.class.st | 21 ++++ exercises/etl/EtlTest.class.st | 123 ++++++++++++++++++++++ exercises/etl/README.md | 77 ++++++++++++++ 4 files changed, 222 insertions(+) create mode 100644 exercises/etl/.meta/hints.md create mode 100644 exercises/etl/.meta/solution/Etl.class.st create mode 100644 exercises/etl/EtlTest.class.st create mode 100644 exercises/etl/README.md diff --git a/exercises/etl/.meta/hints.md b/exercises/etl/.meta/hints.md new file mode 100644 index 00000000..f96e7358 --- /dev/null +++ b/exercises/etl/.meta/hints.md @@ -0,0 +1 @@ +While readability is preferred over efficiency, there is a solution that avoids multiple input looksups when solving this. \ No newline at end of file diff --git a/exercises/etl/.meta/solution/Etl.class.st b/exercises/etl/.meta/solution/Etl.class.st new file mode 100644 index 00000000..d2465a77 --- /dev/null +++ b/exercises/etl/.meta/solution/Etl.class.st @@ -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 ] +] diff --git a/exercises/etl/EtlTest.class.st b/exercises/etl/EtlTest.class.st new file mode 100644 index 00000000..72238732 --- /dev/null +++ b/exercises/etl/EtlTest.class.st @@ -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) +] diff --git a/exercises/etl/README.md b/exercises/etl/README.md new file mode 100644 index 00000000..8f51556c --- /dev/null +++ b/exercises/etl/README.md @@ -0,0 +1,77 @@ +# 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. + + +## Downloading + +To download this exercise in Pharo, type: `etl` into the `Exercism | Fetch Exercise` package menu prompt (right click on the Exercism package in the Pharo System Browser). You can also submit your solution from the same menu for any selected package. You don't normally need to use the exercism cli (as indicated on the right hand panel). + +## Running The Tests + +Tests can be run directly from the Pharo IDE, by clicking on the test orb next to any test. +The SUnit convention is that the provided `EtlTest`, will test the functionality of `Etl`. + +If you are still stuck, the track documentation has more detailed help on [running tests](https://exercism.io/tracks/pharo/tests). + +## Language and Environment Help + +For Pharo installation and learning resources, refer to the [track help page](https://exercism.io/tracks/pharo/learning). + + +## Source + +The Jumpstart Lab team [http://jumpstartlab.com](http://jumpstartlab.com) + + +## Submitting Incomplete Solutions + +Remember, it is also possible to submit an incomplete solution so you can see how others have completed this exercise and can learn from their approach.