From 526f22c3293a4c25ac73edc0df32ce35976baf28 Mon Sep 17 00:00:00 2001 From: Robbie Antenesse Date: Sun, 17 Dec 2017 13:48:48 -0700 Subject: [PATCH] Add letter distribution and word length stats --- src/Helpers.js | 63 ++++++++++- .../DictionaryDetails/StatsSection.jsx | 107 ++++++++++++++++++ .../display/DictionaryDetails/index.jsx | 25 +--- src/index.jsx | 8 +- src/managers/Updater.js | 5 +- 5 files changed, 180 insertions(+), 28 deletions(-) create mode 100644 src/components/display/DictionaryDetails/StatsSection.jsx diff --git a/src/Helpers.js b/src/Helpers.js index e6c95fa..958b33c 100644 --- a/src/Helpers.js +++ b/src/Helpers.js @@ -109,7 +109,7 @@ export function characterIsUppercase (character) { return character === character.toUpperCase(); } -export function getWordsStats (words, partsOfSpeech) { +export function getWordsStats (words, partsOfSpeech, isCaseSensitive = false) { const wordStats = { numberOfWords: [ { @@ -117,6 +117,19 @@ export function getWordsStats (words, partsOfSpeech) { value: words.length, }, ], + wordLength: { + shortest: 0, + longest: 0, + average: 0, + }, + letterDistribution: [ + // { + // letter: '', + // number: 0, + // percentage: 0.00, + // } + ], + totalLetters: 0, }; partsOfSpeech.forEach(partOfSpeech => { @@ -132,5 +145,53 @@ export function getWordsStats (words, partsOfSpeech) { value: words.filter(word => !partsOfSpeech.includes(word.partOfSpeech)).length, }); + let totalLetters = 0; + const numberOfLetters = {}; + + words.forEach(word => { + const shortestWord = wordStats.wordLength.shortest; + const longestWord = wordStats.wordLength.longest; + const wordLetters = word.name.split(''); + const lettersInWord = wordLetters.length; + + totalLetters += lettersInWord; + + if (shortestWord === 0 || lettersInWord < shortestWord) { + wordStats.wordLength.shortest = lettersInWord; + } + + if (longestWord === 0 || lettersInWord > longestWord) { + wordStats.wordLength.longest = lettersInWord; + } + + wordLetters.forEach(letter => { + const letterToUse = isCaseSensitive ? letter : letter.toLowerCase(); + if (!numberOfLetters.hasOwnProperty(letterToUse)) { + numberOfLetters[letterToUse] = 1; + } else { + numberOfLetters[letterToUse]++; + } + }); + }); + + wordStats.totalLetters = totalLetters; + wordStats.wordLength.average = totalLetters / words.length; + + for (const letter in numberOfLetters) { + if (numberOfLetters.hasOwnProperty(letter)) { + const number = numberOfLetters[letter]; + wordStats.letterDistribution.push({ + letter, + number, + percentage: number / totalLetters, + }); + } + } + + wordStats.letterDistribution.sort((a, b) => { + if (a.percentage === b.percentage) return 0; + return (a.percentage > b.percentage) ? -1 : 1; + }); + return wordStats; } diff --git a/src/components/display/DictionaryDetails/StatsSection.jsx b/src/components/display/DictionaryDetails/StatsSection.jsx new file mode 100644 index 0000000..994c6de --- /dev/null +++ b/src/components/display/DictionaryDetails/StatsSection.jsx @@ -0,0 +1,107 @@ +import Inferno from 'inferno'; +import PropTypes from 'prop-types'; + +export const StatsSection = (props) => { + PropTypes.checkPropTypes({ + stats: PropTypes.object.isRequired, + }, props, 'prop', 'StatsSection'); + + const { + numberOfWords, + wordLength, + letterDistribution, + totalLetters + } = props.stats; + + return ( +
+
+
+ Number of Words +
+ {numberOfWords.map(stat => { + return ( +
+
+ + { stat.name } + + + { stat.value } + +
+
+ ); + })} +
+
+
+ +
+
+ Word Length +
+ +
+
+ + Shortest + + + { wordLength.shortest } + +
+
+ +
+
+ + Longest + + + { wordLength.longest } + +
+
+ +
+
+ + Average + + + { wordLength.average } + +
+
+ +
+
+
+ +
+
+ Letter Distribution +
+ {letterDistribution.map(stat => { + return ( +
+
+ + { stat.letter } + + + { stat.percentage.toFixed(2) } + +
+
+ ); + })} +
+ { totalLetters } Total Letters +
+
+ +
+ ); +} diff --git a/src/components/display/DictionaryDetails/index.jsx b/src/components/display/DictionaryDetails/index.jsx index 25f1b5e..c83d802 100644 --- a/src/components/display/DictionaryDetails/index.jsx +++ b/src/components/display/DictionaryDetails/index.jsx @@ -8,6 +8,7 @@ import './styles.scss'; import { EditDictionaryModal } from '../../management/EditDictionaryModal'; import { DetailsSection } from './DetailsSection'; +import { StatsSection } from './StatsSection'; const DISPLAY = { NONE: false, @@ -28,7 +29,7 @@ export class DictionaryDetails extends Component { alphabeticalOrder: PropTypes.array, details: PropTypes.object, settings: PropTypes.object, - stats: PropTypes.array, + stats: PropTypes.object, updater: PropTypes.object, updateDisplay: PropTypes.func, }, props, 'prop', 'DictionaryDetails'); @@ -88,27 +89,7 @@ export class DictionaryDetails extends Component { case DISPLAY.STATS : { displayJSX = ( -
-
- Number of Words -
- {this.props.stats.numberOfWords.map(stat => { - return ( -
-
- - { stat.name } - - - { stat.value } - -
-
- ); - })} -
-
-
+ ); break; } diff --git a/src/index.jsx b/src/index.jsx index edd7a44..9dfb0b8 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -29,7 +29,7 @@ class App extends Component { partsOfSpeech: dictionary.partsOfSpeech, details: dictionary.details, settings: dictionary.settings, - stats: [], + stats: {}, alphabeticalOrder: dictionary.alphabeticalOrder, displayedWords: [], @@ -71,15 +71,15 @@ class App extends Component { }); } - updateDisplayedWords () { + updateDisplayedWords (callback = () => {}) { // const {searchIn, searchTerm, filteredPartsOfSpeech} = this.state.searchConfig; // TODO: Sort out searching to remove this temporary solution. dictionary.wordsPromise.then(words => { this.setState({ displayedWords: words, - stats: getWordsStats(words, this.state.partsOfSpeech), - }); + stats: getWordsStats(words, this.state.partsOfSpeech, this.state.settings.caseSensitive), + }, () => callback()); }); } diff --git a/src/managers/Updater.js b/src/managers/Updater.js index 808515f..351a105 100644 --- a/src/managers/Updater.js +++ b/src/managers/Updater.js @@ -58,12 +58,15 @@ export class Updater { updatedDetails['settings'] = this.dictionary.settings; } - console.log(updatedDetails); + // console.log(updatedDetails); if (updatedDetails.isEmpty()) { reject('No dictionary details have changed.'); } else { this.app.setState(updatedDetails, () => { + if (updatedDetails.hasOwnProperty('settings')) { + this.app.updateDisplayedWords(); + } resolve(); }); }