Comparing version 0.1.0 to 0.4.2
{ | ||
"name": "mf-parser", | ||
"version": "0.1.0", | ||
"description": "Parse molecular formula", | ||
"main": "./src/index.js", | ||
"version": "0.4.2", | ||
"description": "", | ||
"main": "src/index.js", | ||
"files": [ | ||
"src" | ||
], | ||
"scripts": { | ||
"eslint": "eslint src test", | ||
"eslint-fix": "npm run eslint -- --fix", | ||
"test": "run-s testonly eslint", | ||
"test-travis": "eslint src && jest --coverage && codecov", | ||
"testonly": "jest", | ||
"build": "cheminfo build --no-source-map" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/cheminfo-js/mf-parser.git" | ||
"url": "git+https://github.com/cheminfo-js/mf-parser.git" | ||
}, | ||
"keywords": [], | ||
"author": "Luc Patiny <luc@patiny.com>", | ||
"author": "Luc Patiny", | ||
"license": "MIT", | ||
@@ -27,15 +19,7 @@ "bugs": { | ||
}, | ||
"homepage": "https://github.com/cheminfo-js/mf-parser#readme", | ||
"jest": { | ||
"testEnvironment": "node" | ||
}, | ||
"devDependencies": { | ||
"cheminfo-tools": "^1.20.1", | ||
"codecov": "^3.0.0", | ||
"eslint": "^4.12.0", | ||
"eslint-config-cheminfo": "^1.8.0", | ||
"eslint-plugin-no-only-tests": "^2.0.0", | ||
"jest": "^21.2.1", | ||
"npm-run-all": "^4.1.2" | ||
"homepage": "https://github.com/cheminfo-js/mf-parser/tree/master/packages/mf-parser#readme", | ||
"dependencies": { | ||
"atom-sorter": "^0.4.2", | ||
"molecular-formula-generator": "^0.0.0" | ||
} | ||
} |
# mf-parser | ||
[![NPM version][npm-image]][npm-url] | ||
[![build status][travis-image]][travis-url] | ||
[![Test coverage][codecov-image]][codecov-url] | ||
[![David deps][david-image]][david-url] | ||
[![npm download][download-image]][download-url] | ||
Parse molecular formula | ||
. | ||
## Installation | ||
`$ npm install mf-parser` | ||
`$ npm install --save mf-parser` | ||
## [API Documentation](https://cheminfo-js.github.io/mf-parser/) | ||
## Usage | ||
## Example | ||
```js | ||
const parseToHtml = require('mf-parser').parseToHtml; | ||
let html = parseToHtml('Et3N . 2HCl); | ||
``` | ||
import library from 'mf-parser'; | ||
You may also be interested to parse first the MF and then retrieve display representation | ||
```js | ||
const MFParser = require('mf-parser'); | ||
let parsed = MFParser.parse('Et3N . 2HCl'); | ||
let displayed = MFParser.toDisplay(parsed); | ||
// displayed could be used to ocreate a custom renderer | ||
console.log(displayed); | ||
const result = library(args); | ||
// result is ... | ||
``` | ||
## License | ||
[MIT](./LICENSE) | ||
[MIT](./LICENSE) | ||
[npm-image]: https://img.shields.io/npm/v/mf-parser.svg?style=flat-square | ||
[npm-url]: https://www.npmjs.com/package/mf-parser | ||
[travis-image]: https://img.shields.io/travis/cheminfo-js/mf-parser/master.svg?style=flat-square | ||
[travis-url]: https://travis-ci.org/cheminfo-js/mf-parser | ||
[codecov-image]: https://img.shields.io/codecov/c/github/cheminfo-js/mf-parser.svg?style=flat-square | ||
[codecov-url]: https://codecov.io/gh/cheminfo-js/mf-parser | ||
[david-image]: https://img.shields.io/david/cheminfo-js/mf-parser.svg?style=flat-square | ||
[david-url]: https://david-dm.org/cheminfo-js/mf-parser | ||
[download-image]: https://img.shields.io/npm/dm/mf-parser.svg?style=flat-square | ||
[download-url]: https://www.npmjs.com/package/mf-parser |
@@ -5,15 +5,140 @@ 'use strict'; | ||
test('MF', () => { | ||
test('MF of Et3N.HCl', () => { | ||
var mf = new MF('Et3N.HCl'); | ||
var parts = mf.toParts(); | ||
expect(parts).toEqual( | ||
[[ | ||
{kind: 'atom', value: 'Et', multiplier: 3}, | ||
{kind: 'atom', value: 'C', multiplier: 6}, | ||
{kind: 'atom', value: 'H', multiplier: 15}, | ||
{kind: 'atom', value: 'N', multiplier: 1} | ||
], [ | ||
{kind: 'atom', value: 'Cl', multiplier: 1}, | ||
{kind: 'atom', value: 'H', multiplier: 1}, | ||
{kind: 'atom', value: 'Cl', multiplier: 1} | ||
]] | ||
); | ||
var newMF = mf.toMF(); | ||
expect(newMF).toBe('C6H15N.HCl'); | ||
mf.canonize(); | ||
let html = mf.toHtml(); | ||
expect(html).toBe('C<sub>6</sub>H<sub>15</sub>N<sub>1</sub> • H<sub>1</sub>Cl<sub>1</sub>'); | ||
let info = mf.getInfo(); | ||
expect(info).toEqual({parts: | ||
[{mass: 101.19022990269394, | ||
monoisotopicMass: 101.12044948788001, | ||
charge: 0, | ||
mf: 'C6H15N'}, | ||
{mass: 36.460878336663775, | ||
monoisotopicMass: 35.97667771423, | ||
charge: 0, | ||
mf: 'HCl'}], | ||
monoisotopicMass: 137.09712720211002, | ||
mass: 137.6511082393577, | ||
charge: 0, | ||
mf: 'C6H15N.HCl'} | ||
); | ||
}); | ||
test('MF of (Me2CH)3N no expand', () => { | ||
var mf = new MF('(Me2CH)3N'); | ||
var parts = mf.toParts({expand: false}); | ||
expect(parts).toEqual( | ||
[[ | ||
{kind: 'atom', value: 'C', multiplier: 3}, | ||
{kind: 'atom', value: 'H', multiplier: 3}, | ||
{kind: 'atom', value: 'Me', multiplier: 6}, | ||
{kind: 'atom', value: 'N', multiplier: 1} | ||
]] | ||
); | ||
var newMF = mf.toMF(); | ||
expect(newMF).toBe('C3H3Me6N'); | ||
let info = mf.getInfo(); | ||
expect(info).toEqual( | ||
{mass: 143.27008211723435, | ||
monoisotopicMass: 143.16739968126, | ||
charge: 0, | ||
mf: 'C3H3Me6N' | ||
} | ||
); | ||
}); | ||
test('MF of (+)SO4(+)(-2)2', () => { | ||
var mf = new MF('(+)SO4(+)(-2)2'); | ||
var parts = mf.toParts(); | ||
expect(parts).toEqual( | ||
[[ | ||
{kind: 'atom', value: 'O', multiplier: 4}, | ||
{kind: 'atom', value: 'S', multiplier: 1}, | ||
{kind: 'charge', value: -2}, | ||
]] | ||
); | ||
var newMF = mf.toMF(); | ||
expect(newMF).toBe('O4S(-2)'); | ||
let info = mf.getInfo(); | ||
expect(info).toEqual({ | ||
monoisotopicMass: 95.95172965268, | ||
mass: 96.06240710340018, | ||
charge: -2, | ||
observedMonoisotopicMass: 47.97641340624907, | ||
mf: 'O4S(-2)'} | ||
); | ||
}); | ||
test('MF of NC[13C][15N]2NN2', () => { | ||
var mf = new MF('NC[13C][15N]2NN2'); | ||
var parts = mf.toParts(); | ||
expect(parts).toEqual( | ||
[[ | ||
{kind: 'atom', value: 'C', multiplier: 1}, | ||
{kind: 'isotope', value: {atom: 'C', isotope: 13}, multiplier: 1}, | ||
{kind: 'atom', value: 'N', multiplier: 4}, | ||
{kind: 'isotope', value: {atom: 'N', isotope: 15}, multiplier: 2} | ||
]] | ||
); | ||
let info = mf.getInfo(); | ||
expect(info).toEqual({ | ||
monoisotopicMass: 111.01586865055, | ||
mass: 111.04112137534844, | ||
charge: 0, | ||
mf: 'C[13C]N4[15N]2'} | ||
); | ||
var newMF = mf.toMF(); | ||
expect(newMF).toBe('C[13C]N4[15N]2'); | ||
}); | ||
test('MF of CC{50,50}H', () => { | ||
var mf = new MF('HC{50,50}C'); | ||
var parts = mf.toParts(); | ||
expect(parts).toEqual( | ||
[[ | ||
{kind: 'atom', value: 'C', multiplier: 1}, | ||
{kind: 'isotopeRatio', value: {atom: 'C', ratio: [50, 50]}, multiplier: 1}, | ||
{kind: 'atom', value: 'H', multiplier: 1} | ||
]] | ||
); mf.canonize(); var toHtml = mf.toHtml(); | ||
expect(toHtml).toBe('Et<sub>3</sub>N<sub>1</sub> • Cl<sub>1</sub>H<sub>1</sub>'); | ||
); | ||
var newMF = mf.toMF(); | ||
expect(newMF).toBe('CC{50,50}H'); | ||
let info = mf.getInfo(); | ||
expect(info).toEqual({ | ||
monoisotopicMass: 25.00782503223, | ||
mass: 25.520354068326025, | ||
charge: 0, | ||
mf: 'CC{50,50}H'} | ||
); | ||
}); |
@@ -20,2 +20,6 @@ 'use strict'; | ||
'C(--)': [{kind: 'atom', value: 'C'}, {kind: 'charge', value: -2}], | ||
'(H+)': [{kind: 'openingParenthesis', value: '('}, {kind: 'atom', value: 'H'}, {kind: 'charge', value: 1}, {kind: 'closingParenthesis', value: ')'}], | ||
C$ABC: [{kind: 'atom', value: 'C'}, {kind: 'comment', value: 'ABC'}], | ||
'C(-1)(-3)': [{kind: 'atom', value: 'C'}, {kind: 'charge', value: -1}, {kind: 'charge', value: -3}], | ||
'C(-1)2(-3)3': [{kind: 'atom', value: 'C'}, {kind: 'charge', value: -1}, {kind: 'multiplier', value: 2}, {kind: 'charge', value: -3}, {kind: 'multiplier', value: 3}], | ||
'C(-2)': [{kind: 'atom', value: 'C'}, {kind: 'charge', value: -2}], | ||
@@ -29,4 +33,3 @@ 'C(H-2)': [{kind: 'atom', value: 'C'}, {kind: 'openingParenthesis', value: '('}, {kind: 'atom', value: 'H'}, {kind: 'multiplier', value: -2}, {kind: 'closingParenthesis', value: ')'}], | ||
test('parse molecular formula', function () { | ||
test.only('parse molecular formula', function () { | ||
for (var key of Object.keys(tests)) { | ||
@@ -43,2 +46,3 @@ check(key, tests[key]); | ||
function check(mf, result) { | ||
@@ -45,0 +49,0 @@ var parsed = parse(mf); |
@@ -18,3 +18,4 @@ 'use strict'; | ||
MULTIPLIER: 'multiplier', | ||
TEXT: 'text' | ||
TEXT: 'text', | ||
COMMENT: 'comment' | ||
}; |
@@ -8,2 +8,4 @@ 'use strict'; | ||
const toParts = require('./util/toParts'); | ||
const getInfo = require('./util/getInfo'); | ||
const partsToMF = require('./util/partsToMF'); | ||
const partsToDisplay = require('./util/partsToDisplay'); | ||
@@ -30,5 +32,5 @@ | ||
toParts() { | ||
toParts(options) { | ||
if (!this.cache.parts) { | ||
this.cache.parts = toParts(this.parsed); | ||
this.cache.parts = toParts(this.parsed, options); | ||
} | ||
@@ -38,2 +40,25 @@ return this.cache.parts; | ||
/** | ||
* Returns an object with the global MF, global charge, monoisotopic mass and mass | ||
* as well as the same informations for all the parts | ||
*/ | ||
getInfo() { | ||
if (!this.cache.info) { | ||
this.toParts(); | ||
this.cache.info = getInfo(this.cache.parts); | ||
} | ||
return this.cache.info; | ||
} | ||
/** | ||
* Get a canonized MF | ||
*/ | ||
toMF() { | ||
if (!this.cache.mf) { | ||
this.toParts(); | ||
this.cache.mf = partsToMF(this.cache.parts); | ||
} | ||
return this.cache.mf; | ||
} | ||
canonize() { | ||
@@ -40,0 +65,0 @@ this.toParts(); |
@@ -88,2 +88,5 @@ 'use strict'; | ||
this.result.push({kind: Kind.CHARGE, value: charge}); | ||
} else if (char === '$') { // it is a comment after | ||
this.result.push({kind: Kind.COMMENT, value: this.mf.substring(this.i + 1)}); | ||
break; | ||
} else { | ||
@@ -96,3 +99,2 @@ this.result.push({kind: Kind.TEXT, value: char}); | ||
this.checkParenthesis(); | ||
return this.result; | ||
@@ -191,3 +193,4 @@ } | ||
ascii = this.mf.charCodeAt(this.i); | ||
} while (ascii === 43 || ascii === 45 || (ascii > 47 && ascii < 58)); // closing parenthesis | ||
} while (ascii === 43 || ascii === 45 || (ascii > 47 && ascii < 58)); | ||
this.i--; | ||
return parseCharge(substring); | ||
@@ -194,0 +197,0 @@ } |
@@ -34,10 +34,11 @@ 'use strict'; | ||
{ | ||
mf: 'Et3N.HCl', | ||
mf: 'Et3N.ClH', | ||
result: [ | ||
[ | ||
{kind: 'atom', value: 'Et', multiplier: 3}, | ||
{kind: 'atom', value: 'C', multiplier: 6}, | ||
{kind: 'atom', value: 'H', multiplier: 15}, | ||
{kind: 'atom', value: 'N', multiplier: 1} | ||
], [ | ||
{kind: 'atom', value: 'Cl', multiplier: 1}, | ||
{kind: 'atom', value: 'H', multiplier: 1} | ||
{kind: 'atom', value: 'H', multiplier: 1}, | ||
{kind: 'atom', value: 'Cl', multiplier: 1} | ||
] | ||
@@ -44,0 +45,0 @@ ] |
'use strict'; | ||
const Kind = require('../Kind'); | ||
const groups = require('chemical-elements/src/groupsObject.js'); | ||
const atomSorter = require('atom-sorter'); | ||
module.exports = function toParts(lines) { | ||
/** | ||
* | ||
* @param {*} lines | ||
* @param {object} options | ||
* @param {boolean} [true] options.expand - Should we expand the groups | ||
*/ | ||
module.exports = function toParts(lines, options = {}) { | ||
const { | ||
expand: shouldExpandGroups = true | ||
} = options; | ||
let parts = []; | ||
@@ -11,3 +23,2 @@ | ||
parts.push(currentPart); | ||
for (let line of lines) { | ||
@@ -40,4 +51,6 @@ switch (line.kind) { | ||
break; | ||
case Kind.COMMENT: // we ignore comments to create the parts and canonized MF | ||
break; | ||
default: | ||
throw new Error('Can not process mf having ', line.kind); | ||
throw new Error('Can not process mf having: ' + line.kind); | ||
} | ||
@@ -47,4 +60,4 @@ previousKind = line.kind; | ||
globalPartMultiplier(currentPart); | ||
return combineAtoms(parts); | ||
if (shouldExpandGroups) expandGroups(parts); | ||
return combineAtomsIsotopesCharges(parts); | ||
}; | ||
@@ -99,3 +112,27 @@ | ||
function combineAtoms(parts) { | ||
function expandGroups(parts) { | ||
for (let part of parts) { | ||
let expanded = false; | ||
for (let i = 0; i < part.lines.length; i++) { | ||
let line = part.lines[i]; | ||
if (line.kind === Kind.ATOM) { | ||
let group = groups[line.value]; | ||
if (group) { | ||
expanded = true; | ||
for (let element of group.elements) { | ||
part.lines.push({ | ||
kind: 'atom', | ||
value: element.symbol, | ||
multiplier: line.multiplier * element.number | ||
}); | ||
} | ||
part.lines[i] = undefined; | ||
} | ||
} | ||
} | ||
if (expanded) part.lines = part.lines.filter((a) => a); | ||
} | ||
} | ||
function combineAtomsIsotopesCharges(parts) { | ||
let results = []; | ||
@@ -107,8 +144,15 @@ for (let part of parts) { | ||
// Kind.ATOM | ||
{ | ||
let currentKey = ''; | ||
for (let key of part.keys) { | ||
let currentKey = ''; | ||
for (let key of part.keys) { | ||
if (key.key === Kind.CHARGE) { | ||
if (currentKey !== key.key) { | ||
currentKey = key.key; | ||
result.push({ | ||
kind: Kind.CHARGE, | ||
value: key.value.value * key.value.multiplier | ||
}); | ||
} else { | ||
result[result.length - 1].value += key.value.value * key.value.multiplier; | ||
} | ||
} else { | ||
if (currentKey !== key.key) { | ||
result.push(key.value); | ||
@@ -119,3 +163,21 @@ } else { | ||
} | ||
currentKey = key.key; | ||
} | ||
result.sort((a, b) => { | ||
if (a.kind === Kind.CHARGE) return 1; | ||
if (b.kind === Kind.CHARGE) return -1; | ||
let atomA = a.kind === Kind.ATOM ? a.value : a.value.atom; | ||
let atomB = b.kind === Kind.ATOM ? b.value : b.value.atom; | ||
if (atomA !== atomB) return atomSorter(atomA, atomB); | ||
// same atome but some isotopes ... | ||
if (a.kind === Kind.ATOM) return -1; | ||
if (b.kind === Kind.ATOM) return 1; | ||
if (a.kind === Kind.ISOTOPE) return -1; | ||
if (b.kind === Kind.ISOTOPE) return 1; | ||
if (a.kind === Kind.ISOTOPE_RATIO) return -1; | ||
if (b.kind === Kind.ISOTOPE_RATIO) return 1; | ||
return 0; | ||
}); | ||
} | ||
@@ -138,8 +200,14 @@ return results; | ||
let key = [line.kind]; | ||
if (typeof line.value === 'string') { | ||
key.push(line.value); | ||
} else { | ||
for (let prop of Object.keys(line.value).sort()) { | ||
key.push(line.value[prop]); | ||
} | ||
switch (line.kind) { | ||
case (Kind.CHARGE): | ||
break; | ||
default: | ||
if (typeof line.value === 'string') { | ||
key.push(line.value); | ||
} else { | ||
for (let prop of Object.keys(line.value).sort()) { | ||
key.push(line.value[prop]); | ||
} | ||
} | ||
} | ||
@@ -146,0 +214,0 @@ return key.join('-'); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
43075
0
25
1125
2
29
+ Addedatom-sorter@^0.4.2
+ Addedatom-sorter@0.4.3(transitive)