Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-scripts": "4.0.1",
"web-vitals": "^0.2.4"
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^15.0.4",
"@testing-library/user-event": "^14.5.2",
"react": "^18.3.0",
"react-dom": "^18.3.0",
"react-scripts": "^5.0.1",
"web-vitals": "^3.5.2"
},
"scripts": {
"start": "react-scripts start",
Expand Down
26 changes: 23 additions & 3 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,32 @@
import {useState} from "react";
import {transformNumber} from "./numberTransformer.js";

const getOutput = (value) => {
try {
return transformNumber(value);
}
catch (e) {
return 'incorrect';
}
}

function App() {
const text = ''
const [value, setValue] = useState('');

const output = getOutput(value);

return (
<div>
<input type='text' />
<input
type='text'
value={value}
onChange={
(event) => setValue(event.target.value)
}
/>
<div>
<p>
Output: {text}
Output: {output}
</p>
</div>
</div>
Expand Down
93 changes: 88 additions & 5 deletions src/App.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,88 @@
describe('App', () => {
it('should convert number to text', () => {
expect(true).toBe(false)
})
})
import "@testing-library/jest-dom";
import { render, screen, fireEvent, act } from "@testing-library/react";

import App from "./App";

const inputValueAndReturnOutput = (value) => {
const input = screen.getByRole("textbox");

fireEvent.change(input, { target: { value } });

return screen.getByText(/Output:/).textContent;
}

describe("App", () => {
describe('Returning proper numbers', () => {
it('Should render 54', () => {
render(<App />);

act(() => {
const output = inputValueAndReturnOutput('fifty four')

expect(output).toEqual(`Output: 54`);
});
})

it('Should render 2045', () => {
render(<App />);

act(() => {
const output = inputValueAndReturnOutput('two thousand and forty five')

expect(output).toEqual(`Output: 2045`);
});
})

it('Should render 3100090', () => {
render(<App />);

act(() => {
const output = inputValueAndReturnOutput('three million one hundred thousand and ninety')

expect(output).toEqual(`Output: 3100090`);
});
})
});

describe('Returning "incorrect"', () => {
it('Should render incorrect for asdasd', () => {
render(<App />);

act(() => {
const output = inputValueAndReturnOutput('asdasd')

expect(output).toEqual(`Output: incorrect`);
});
})

it('Should render incorrect for one one', () => {
render(<App />);

act(() => {
const output = inputValueAndReturnOutput('one one')

expect(output).toEqual(`Output: incorrect`);
});
})

it('Should render incorrect for fifty fifteen', () => {
render(<App />);

act(() => {
const output = inputValueAndReturnOutput('fifty fifteen')

expect(output).toEqual(`Output: incorrect`);
});
})

it('Should render incorrect for fifteen fifty', () => {
render(<App />);

act(() => {
const output = inputValueAndReturnOutput('fifteen fifty')

expect(output).toEqual(`Output: incorrect`);
});
})
});
});
86 changes: 86 additions & 0 deletions src/numberTransformer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
const FACTORS = { // for each entry here we store multiplier and addition
and: [1, 0], // and is a word we can skip so we multiply by 1 and add 0 to result
};

const BASICS = [
'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight',
'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen',
'sixteen', 'seventeen', 'eighteen', 'nineteen',
]

const TEN_MULTIPLIERS = ['twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety']

const TEN_POWERS = ['hundred', 'thousand', 'million']

BASICS.forEach((number, index) => {
FACTORS[number] = [1, index]; // for each basic values <0, 19> multiply by one and add index which is the same as number
})

TEN_MULTIPLIERS.forEach((number, index) => {
FACTORS[number] = [1, (index + 2) * 10]; // for each ten multiplier we need to increase result by it value multiplied by 10
});

TEN_POWERS.forEach((number, index) => {
FACTORS[number] = [10 ** ((index * 3) || 2), 0]; // when we spot hundred, thousand, etc we need to multiply result by corresponding power of 10
});

const checkIfSiblingsAreFromTheSameGroup = ([previous, current], group) => group.includes(previous) && group.includes(current);

const areSiblingsBasics = (values) => checkIfSiblingsAreFromTheSameGroup(values, BASICS);
const areSiblingsTenMultipliers = (values) => checkIfSiblingsAreFromTheSameGroup(values, TEN_MULTIPLIERS);

const areSiblingsIllegal = (values) => areSiblingsBasics(values) || areSiblingsTenMultipliers(values);

const isTenMultiplierNextToBasicHigherThan9 = ([previous, current]) => {
return (
TEN_MULTIPLIERS.includes(current) && BASICS.includes(previous)
) || (
TEN_MULTIPLIERS.includes(previous) && BASICS.indexOf(current) > 9
)
}

const siblingsGuards = [
areSiblingsIllegal,
isTenMultiplierNextToBasicHigherThan9
];

export const transformNumber = (strNumber) => {
const normalizedText = strNumber.toLowerCase();

const parts = normalizedText.split(' ');

let result = 0;
let currentNumber = 0;
let previous = '';

for (let part of parts) {
if(!part.length) {
continue; // ignore spaces at the end and on the begining
}

if (!Object.keys(FACTORS).includes(part)) {
throw new Error('Incorrect Number');
}

for(let guard of siblingsGuards) {
if(guard([previous, part])) {
throw new Error('Incorrect Number');
}
}

previous = part;

const [multiplyBy, add] = FACTORS[part];

currentNumber = currentNumber * multiplyBy + add

if (multiplyBy <= 100) { // if we multiply by one or one hundred we just accumulating current number (because we can have hundred of thousands)
continue;
}

//when we spot thousand or milion we need to add it to result and start accumulating current number from scratch
result += currentNumber
currentNumber = 0
}
return result + currentNumber
};
Loading