Vue.js 2.0 - Periodic Table solved
solving Periodic Table problem by separate logic from UI component
by Pasit R
HTML
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app" class="container">
<div class="row">
<div class="col-xs-6">
<h4>AddElement </h4>
<div class="form-group">
<label>Symbol:</label>
<input class="form-control" v-model="adding.symbol" />
</div>
<div class="form-group">
<label>Weight:</label>
<input class="form-control" v-model="adding.weight" />
</div>
<div class="form-group">
<label>Row:</label>
<input class="form-control" v-model="adding.row" />
</div>
<div class="form-group">
<label>Col:</label>
<input class="form-control" v-model="adding.col" />
</div>
<button class="btn" v-on:click="addElement">AddElement</button>
</div>
<div class="col-xs-6">
<h4>List of elements</h4>
<ul>
<li v-for="item in elements">{{item.symbol}}: {{item.atomicWeight}}</li>
</ul>
</div>
</div>
<div>
<div class="col-xs-6">
<h4>QueryElement (row, col)</h4>
<input v-model="query.row" placeholder="Row" />
<input v-model="query.col" placeholder="Col" />
<button v-on:click="queryElement" class="btn">Query</button>
</div>
<div class="col-xs-6">
<h4>CalculateMolarWeight (formula)</h4>
<input type="text" v-model="formula" placeholder="eg. H2O" />
<button v-on:click="calculateMolarWeight" class="btn">Calculate</button>
</div>
<div class="col-xs-12">
<h4>Result:</h4>
<pre>{{ message }}</pre>
</div>
</div>
</div>
JavaScript
// Model
class PeriodicTable {
constructor() {
this.items = []
this.formulaPattern = /[A-Z](:?[a-z0-9]*)/g
}
addElement(symbol, atomicWeight, col, row) {
const el = {
symbol: symbol.trim(),
atomicWeight: parseFloat(atomicWeight),
col: parseInt(col, 10),
row: parseInt(row, 10)
}
if (!this.isValidSymbol(el.symbol)) {
throw new Error(`invalid symbol ${symbol}`)
}
if (!el.atomicWeight || el.atomicWeight < 0) {
throw new Error(`invalid weight ${atomicWeight}`)
}
const exists = this.findSymbol(el.symbol)
if (exists) {
throw new Error(`${el.symbol} already exists`)
}
const found = this.queryElement(el.row, el.col)
if (found) {
throw new Error(`${el.row}, ${el.col} already exists`)
}
this.items.push(el)
}
queryElement(row, col) {
let colNum = parseInt(col)
let rowNum = parseInt(row)
let match = (item) => item.col === colNum && item.row === rowNum
var result = this.items.filter(match)
if (result.length === 0) {
return null
}
return result[0].symbol
}
calculateMolarWeight(input) {
const items = this.extractFormula(input)
const sum = (prev, current) => {
let weight = current.weight * current.quantity
return prev + weight
}
const total = items
.map(item => this.extractSymbol(item))
.reduce(sum, 0)
// eliminate binary floating point
return parseFloat(total.toPrecision(10))
}
extractFormula(formula) {
const result = []
let match
while (match = this.formulaPattern.exec(formula)) {
result.push(match[0])
}
return result
}
extractSymbol(formula) {
const pattern = /([A-Z][a-z]*)([0-9]*)/g
let match = pattern.exec(formula)
let symbol = match[1]
let quantity = match[2] ? parseInt(match[2], 10) : 1
let weight = this.getAtomicWeight(symbol)
return {
symbol,
quantity,
weight
}
}
...