feat(tool): roman-arabic numbers converter
This commit is contained in:
		
							parent
							
								
									3ae61147a9
								
							
						
					
					
						commit
						655019cf23
					
				| @ -4,6 +4,7 @@ import type { ToolCategory } from './Tool'; | |||||||
| import { tool as tokenGenerator } from './token-generator'; | import { tool as tokenGenerator } from './token-generator'; | ||||||
| import { tool as hashText } from './hash-text'; | import { tool as hashText } from './hash-text'; | ||||||
| import { tool as uuidGenerator } from './uuid-generator'; | import { tool as uuidGenerator } from './uuid-generator'; | ||||||
|  | import { tool as romanNumeralConverter } from './roman-numeral-converter'; | ||||||
| 
 | 
 | ||||||
| export const toolsByCategory: ToolCategory[] = [ | export const toolsByCategory: ToolCategory[] = [ | ||||||
|   { |   { | ||||||
| @ -11,6 +12,11 @@ export const toolsByCategory: ToolCategory[] = [ | |||||||
|     icon: LockOpen, |     icon: LockOpen, | ||||||
|     components: [tokenGenerator, hashText, uuidGenerator], |     components: [tokenGenerator, hashText, uuidGenerator], | ||||||
|   }, |   }, | ||||||
|  |   { | ||||||
|  |     name: 'Converter', | ||||||
|  |     icon: LockOpen, | ||||||
|  |     components: [romanNumeralConverter], | ||||||
|  |   }, | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| export const tools = toolsByCategory.flatMap(({ components }) => components); | export const tools = toolsByCategory.flatMap(({ components }) => components); | ||||||
|  | |||||||
							
								
								
									
										11
									
								
								src/tools/roman-numeral-converter/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/tools/roman-numeral-converter/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | import { LetterX } from '@vicons/tabler'; | ||||||
|  | import type { ITool } from '../Tool'; | ||||||
|  | 
 | ||||||
|  | export const tool: ITool = { | ||||||
|  |   name: 'Roman numeral converter', | ||||||
|  |   path: '/roman-numeral-converter', | ||||||
|  |   description: 'Convert Roman numerals to numbers and convert numbers to Roman numerals.', | ||||||
|  |   keywords: ['roman', 'arabic', 'converter', 'X', 'I', 'V', 'L', 'C', 'D', 'M'], | ||||||
|  |   component: () => import('./roman-numeral-converter.vue'), | ||||||
|  |   icon: LetterX, | ||||||
|  | }; | ||||||
| @ -0,0 +1,73 @@ | |||||||
|  | import { expect, describe, it } from 'vitest'; | ||||||
|  | import { arabicToRoman } from './roman-numeral-converter.service'; | ||||||
|  | 
 | ||||||
|  | describe('roman-numeral-converter', () => { | ||||||
|  |   describe('arabicToRoman', () => { | ||||||
|  |     it('should convert numbers lower than 1 to empty string', () => { | ||||||
|  |       expect(arabicToRoman(-100)).toEqual(''); | ||||||
|  |       expect(arabicToRoman(-42)).toEqual(''); | ||||||
|  |       expect(arabicToRoman(-26)).toEqual(''); | ||||||
|  |       expect(arabicToRoman(-10)).toEqual(''); | ||||||
|  |       expect(arabicToRoman(0)).toEqual(''); | ||||||
|  |       expect(arabicToRoman(0.5)).toEqual(''); | ||||||
|  |       expect(arabicToRoman(0.9)).toEqual(''); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should convert floating points number to the lower integer in roman version', () => { | ||||||
|  |       expect(arabicToRoman(-100)).toEqual(''); | ||||||
|  |       expect(arabicToRoman(-42)).toEqual(''); | ||||||
|  |       expect(arabicToRoman(-26)).toEqual(''); | ||||||
|  |       expect(arabicToRoman(-10)).toEqual(''); | ||||||
|  |       expect(arabicToRoman(0)).toEqual(''); | ||||||
|  |       expect(arabicToRoman(0.5)).toEqual(''); | ||||||
|  |       expect(arabicToRoman(0.9)).toEqual(''); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should convert positive integers to roman numbers', () => { | ||||||
|  |       expect(arabicToRoman(1)).toEqual('I'); | ||||||
|  |       expect(arabicToRoman(2)).toEqual('II'); | ||||||
|  |       expect(arabicToRoman(3)).toEqual('III'); | ||||||
|  |       expect(arabicToRoman(4)).toEqual('IV'); | ||||||
|  |       expect(arabicToRoman(5)).toEqual('V'); | ||||||
|  |       expect(arabicToRoman(6)).toEqual('VI'); | ||||||
|  |       expect(arabicToRoman(7)).toEqual('VII'); | ||||||
|  |       expect(arabicToRoman(8)).toEqual('VIII'); | ||||||
|  |       expect(arabicToRoman(9)).toEqual('IX'); | ||||||
|  |       expect(arabicToRoman(10)).toEqual('X'); | ||||||
|  |       expect(arabicToRoman(11)).toEqual('XI'); | ||||||
|  |       expect(arabicToRoman(12)).toEqual('XII'); | ||||||
|  |       expect(arabicToRoman(13)).toEqual('XIII'); | ||||||
|  |       expect(arabicToRoman(14)).toEqual('XIV'); | ||||||
|  |       expect(arabicToRoman(15)).toEqual('XV'); | ||||||
|  |       expect(arabicToRoman(16)).toEqual('XVI'); | ||||||
|  |       expect(arabicToRoman(17)).toEqual('XVII'); | ||||||
|  |       expect(arabicToRoman(18)).toEqual('XVIII'); | ||||||
|  |       expect(arabicToRoman(19)).toEqual('XIX'); | ||||||
|  |       expect(arabicToRoman(20)).toEqual('XX'); | ||||||
|  |       expect(arabicToRoman(21)).toEqual('XXI'); | ||||||
|  |       expect(arabicToRoman(24)).toEqual('XXIV'); | ||||||
|  |       expect(arabicToRoman(28)).toEqual('XXVIII'); | ||||||
|  |       expect(arabicToRoman(29)).toEqual('XXIX'); | ||||||
|  |       expect(arabicToRoman(30)).toEqual('XXX'); | ||||||
|  |       expect(arabicToRoman(40)).toEqual('XL'); | ||||||
|  |       expect(arabicToRoman(50)).toEqual('L'); | ||||||
|  |       expect(arabicToRoman(60)).toEqual('LX'); | ||||||
|  |       expect(arabicToRoman(70)).toEqual('LXX'); | ||||||
|  |       expect(arabicToRoman(80)).toEqual('LXXX'); | ||||||
|  |       expect(arabicToRoman(90)).toEqual('XC'); | ||||||
|  |       expect(arabicToRoman(100)).toEqual('C'); | ||||||
|  |       expect(arabicToRoman(200)).toEqual('CC'); | ||||||
|  |       expect(arabicToRoman(300)).toEqual('CCC'); | ||||||
|  |       expect(arabicToRoman(400)).toEqual('CD'); | ||||||
|  |       expect(arabicToRoman(500)).toEqual('D'); | ||||||
|  |       expect(arabicToRoman(600)).toEqual('DC'); | ||||||
|  |       expect(arabicToRoman(700)).toEqual('DCC'); | ||||||
|  |       expect(arabicToRoman(800)).toEqual('DCCC'); | ||||||
|  |       expect(arabicToRoman(900)).toEqual('CM'); | ||||||
|  |       expect(arabicToRoman(999)).toEqual('CMXCIX'); | ||||||
|  |       expect(arabicToRoman(1000)).toEqual('M'); | ||||||
|  |       expect(arabicToRoman(2000)).toEqual('MM'); | ||||||
|  |       expect(arabicToRoman(9000)).toEqual('MMMMMMMMM'); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @ -0,0 +1,18 @@ | |||||||
|  | export function arabicToRoman(num: number) { | ||||||
|  |   if (num < 1) return ''; | ||||||
|  | 
 | ||||||
|  |   const lookup: { [key: string]: number } = { M: 1000, CM: 900, D: 500, CD: 400, C: 100, XC: 90, L: 50, XL: 40, X: 10, IX: 9, V: 5, IV: 4, I: 1 }; | ||||||
|  |   let roman = ''; | ||||||
|  |   for (const i in lookup) { | ||||||
|  |     while (num >= lookup[i]) { | ||||||
|  |       roman += i; | ||||||
|  |       num -= lookup[i]; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return roman; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function romanToArabic(s: string) { | ||||||
|  |   const map: { [key: string]: number } = { I: 1, V: 5, X: 10, L: 50, C: 100, D: 500, M: 1000 }; | ||||||
|  |   return [...s].reduce((r, c, i, s) => (map[s[i + 1]] > map[c] ? r - map[c] : r + map[c]), 0); | ||||||
|  | } | ||||||
| @ -0,0 +1,46 @@ | |||||||
|  | <template> | ||||||
|  |     <div> | ||||||
|  |         <n-card title="Arabic to roman"> | ||||||
|  |             <n-space align="center" justify="space-between"> | ||||||
|  |                 <n-input-number | ||||||
|  |                     v-model:value="inputNumeral" | ||||||
|  |                     :min="1" | ||||||
|  |                     style="width: 200px;" | ||||||
|  |                     :show-button="false" | ||||||
|  |                 /> | ||||||
|  |                 <div class="result">{{ outputRoman }}</div> | ||||||
|  |                 <n-button @click="copyRoman" secondary autofocus>Copy</n-button> | ||||||
|  |             </n-space> | ||||||
|  |         </n-card> | ||||||
|  |         <br /> | ||||||
|  |         <n-card title="Roman to arabic"> | ||||||
|  |             <n-space align="center" justify="space-between"> | ||||||
|  |                 <n-input v-model:value="inputRoman" style="width: 200px;" /> | ||||||
|  |                 <div class="result">{{ outputNumeral }}</div> | ||||||
|  |                 <n-button @click="copyArabic" secondary autofocus>Copy</n-button> | ||||||
|  |             </n-space> | ||||||
|  |         </n-card> | ||||||
|  |     </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script setup lang="ts"> | ||||||
|  | import { useCopy } from '@/composable/copy'; | ||||||
|  | import { ref, computed } from 'vue' | ||||||
|  | import { arabicToRoman, romanToArabic } from './roman-numeral-converter.service' | ||||||
|  | 
 | ||||||
|  | const inputNumeral = ref(42) | ||||||
|  | const outputRoman = computed(() => arabicToRoman(inputNumeral.value)) | ||||||
|  | 
 | ||||||
|  | const inputRoman = ref('IVX') | ||||||
|  | const outputNumeral = computed(() => romanToArabic(inputRoman.value)) | ||||||
|  | 
 | ||||||
|  | const { copy: copyRoman } = useCopy({ source: outputRoman, text: 'Roman number copied to the clipboard' }) | ||||||
|  | const { copy: copyArabic } = useCopy({ source: outputNumeral, text: 'Arabic number copied to the clipboard' }) | ||||||
|  | 
 | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .result { | ||||||
|  |     font-size: 22px; | ||||||
|  | } | ||||||
|  | </style> | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user