Comparing version 0.6.3 to 0.6.4
{ | ||
"name": "mf-parser", | ||
"version": "0.6.3", | ||
"version": "0.6.4", | ||
"description": "", | ||
@@ -21,6 +21,7 @@ "main": "src/index.js", | ||
"dependencies": { | ||
"atom-sorter": "^0.6.3", | ||
"chemical-elements": "^0.6.3", | ||
"mf-utilities": "^0.6.3" | ||
"atom-sorter": "^0.6.4", | ||
"chemical-elements": "^0.6.4", | ||
"chemical-groups": "^0.6.4", | ||
"mf-utilities": "^0.6.4" | ||
} | ||
} |
@@ -6,258 +6,279 @@ 'use strict'; | ||
test('MF of C', () => { | ||
var mf = new MF('C'); | ||
var parts = mf.toParts(); | ||
var mf = new MF('C'); | ||
var parts = mf.toParts(); | ||
expect(parts).toEqual([[{ kind: 'atom', value: 'C', multiplier: 1 }]]); | ||
expect(parts).toEqual([[{ kind: 'atom', value: 'C', multiplier: 1 }]]); | ||
var newMF = mf.toMF(); | ||
expect(newMF).toBe('C'); | ||
var newMF = mf.toMF(); | ||
expect(newMF).toBe('C'); | ||
mf.canonize(); | ||
let html = mf.toHtml(); | ||
mf.canonize(); | ||
let html = mf.toHtml(); | ||
expect(html).toBe('C'); | ||
expect(html).toBe('C'); | ||
let info = mf.getInfo(); | ||
let info = mf.getInfo(); | ||
expect(info).toEqual({ | ||
monoisotopicMass: 12, | ||
mass: 12.010735896735248, | ||
charge: 0, | ||
unsaturation: 2, | ||
mf: 'C', | ||
atoms: { | ||
C: 1 | ||
} | ||
}); | ||
expect(info).toEqual({ | ||
monoisotopicMass: 12, | ||
mass: 12.010735896735248, | ||
charge: 0, | ||
unsaturation: 2, | ||
mf: 'C', | ||
atoms: { | ||
C: 1 | ||
} | ||
}); | ||
}); | ||
test('MF of Et3N.HCl', () => { | ||
var mf = new MF('Et3N.HCl'); | ||
var parts = mf.toParts(); | ||
var mf = new MF('Et3N.HCl'); | ||
var parts = mf.toParts(); | ||
expect(parts).toEqual([ | ||
[ | ||
{ kind: 'atom', value: 'C', multiplier: 6 }, | ||
{ kind: 'atom', value: 'H', multiplier: 15 }, | ||
{ kind: 'atom', value: 'N', multiplier: 1 } | ||
], | ||
[ | ||
{ kind: 'atom', value: 'H', multiplier: 1 }, | ||
{ kind: 'atom', value: 'Cl', multiplier: 1 } | ||
] | ||
]); | ||
expect(parts).toEqual([ | ||
[ | ||
{ kind: 'atom', value: 'C', multiplier: 6 }, | ||
{ kind: 'atom', value: 'H', multiplier: 15 }, | ||
{ kind: 'atom', value: 'N', multiplier: 1 } | ||
], | ||
[ | ||
{ kind: 'atom', value: 'H', multiplier: 1 }, | ||
{ kind: 'atom', value: 'Cl', multiplier: 1 } | ||
] | ||
]); | ||
var newMF = mf.toMF(); | ||
expect(newMF).toBe('C6H15N.HCl'); | ||
var newMF = mf.toMF(); | ||
expect(newMF).toBe('C6H15N.HCl'); | ||
mf.canonize(); | ||
let html = mf.toHtml(); | ||
mf.canonize(); | ||
let html = mf.toHtml(); | ||
expect(html).toBe('C<sub>6</sub>H<sub>15</sub>N•HCl'); | ||
expect(html).toBe('C<sub>6</sub>H<sub>15</sub>N•HCl'); | ||
let info = mf.getInfo(); | ||
expect(info).toEqual({ | ||
parts: [ | ||
{ | ||
mass: 101.19022990269394, | ||
monoisotopicMass: 101.12044948788001, | ||
charge: 0, | ||
mf: 'C6H15N', | ||
unsaturation: 0, | ||
atoms: { C: 6, H: 15, N: 1 } | ||
}, | ||
{ | ||
mass: 36.460878336663775, | ||
monoisotopicMass: 35.97667771423, | ||
charge: 0, | ||
mf: 'HCl', | ||
unsaturation: 0, | ||
atoms: { H: 1, Cl: 1 } | ||
} | ||
], | ||
monoisotopicMass: 137.09712720211002, | ||
mass: 137.6511082393577, | ||
let info = mf.getInfo(); | ||
expect(info).toEqual({ | ||
parts: [ | ||
{ | ||
mass: 101.19022990269394, | ||
monoisotopicMass: 101.12044948788001, | ||
charge: 0, | ||
mf: 'C6H15N.HCl' | ||
}); | ||
mf: 'C6H15N', | ||
unsaturation: 0, | ||
atoms: { C: 6, H: 15, N: 1 } | ||
}, | ||
{ | ||
mass: 36.460878336663775, | ||
monoisotopicMass: 35.97667771423, | ||
charge: 0, | ||
mf: 'HCl', | ||
unsaturation: 0, | ||
atoms: { H: 1, Cl: 1 } | ||
} | ||
], | ||
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 }); | ||
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 } | ||
] | ||
]); | ||
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'); | ||
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', | ||
unsaturation: 0, | ||
atoms: { C: 3, H: 3, Me: 6, N: 1 } | ||
}); | ||
let info = mf.getInfo(); | ||
expect(info).toEqual({ | ||
mass: 143.27008211723435, | ||
monoisotopicMass: 143.16739968126, | ||
charge: 0, | ||
mf: 'C3H3Me6N', | ||
unsaturation: 0, | ||
atoms: { C: 3, H: 3, Me: 6, N: 1 } | ||
}); | ||
}); | ||
test('MF of (Me2CH)3N with expand', () => { | ||
var mf = new MF('(Me2CH)3N'); | ||
var parts = mf.toParts({ expand: true }); | ||
var mf = new MF('(Me2CH)3N'); | ||
var parts = mf.toParts({ expand: true }); | ||
expect(parts).toEqual([ | ||
[ | ||
{ kind: 'atom', value: 'C', multiplier: 9 }, | ||
{ kind: 'atom', value: 'H', multiplier: 21 }, | ||
{ kind: 'atom', value: 'N', multiplier: 1 } | ||
] | ||
]); | ||
expect(parts).toEqual([ | ||
[ | ||
{ kind: 'atom', value: 'C', multiplier: 9 }, | ||
{ kind: 'atom', value: 'H', multiplier: 21 }, | ||
{ kind: 'atom', value: 'N', multiplier: 1 } | ||
] | ||
]); | ||
var newMF = mf.toMF(); | ||
expect(newMF).toBe('C9H21N'); | ||
var newMF = mf.toMF(); | ||
expect(newMF).toBe('C9H21N'); | ||
let info = mf.getInfo(); | ||
expect(info).toEqual({ | ||
mass: 143.27008211723435, | ||
monoisotopicMass: 143.16739968126, | ||
charge: 0, | ||
mf: 'C9H21N', | ||
unsaturation: 0, | ||
atoms: { C: 9, H: 21, N: 1 } | ||
}); | ||
let info = mf.getInfo(); | ||
expect(info).toEqual({ | ||
mass: 143.27008211723435, | ||
monoisotopicMass: 143.16739968126, | ||
charge: 0, | ||
mf: 'C9H21N', | ||
unsaturation: 0, | ||
atoms: { C: 9, H: 21, N: 1 } | ||
}); | ||
}); | ||
test('MF of (+)SO4(+)(-2)2', () => { | ||
var mf = new MF('(+)SO4(+)(-2)2'); | ||
var parts = mf.toParts(); | ||
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 } | ||
] | ||
]); | ||
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)'); | ||
var newMF = mf.toMF(); | ||
expect(newMF).toBe('O4S(-2)'); | ||
let info = mf.getInfo({ | ||
customUnsaturations: { | ||
S: 4 | ||
} | ||
}); | ||
expect(info).toEqual({ | ||
monoisotopicMass: 95.95172965268, | ||
mass: 96.06240710340018, | ||
charge: -2, | ||
observedMonoisotopicMass: 47.97641340624907, | ||
mf: 'O4S(-2)', | ||
unsaturation: 4, | ||
atoms: { O: 4, S: 1 } | ||
}); | ||
let info = mf.getInfo({ | ||
customUnsaturations: { | ||
S: 4 | ||
} | ||
}); | ||
expect(info).toEqual({ | ||
monoisotopicMass: 95.95172965268, | ||
mass: 96.06240710340018, | ||
charge: -2, | ||
observedMonoisotopicMass: 47.97641340624907, | ||
mf: 'O4S(-2)', | ||
unsaturation: 4, | ||
atoms: { O: 4, S: 1 } | ||
}); | ||
}); | ||
test('test unsaturation with charges', () => { | ||
expect(new MF('CH4').getInfo().unsaturation).toBe(0); | ||
expect(new MF('C10H22O').getInfo().unsaturation).toBe(0); | ||
expect(new MF('H+').getInfo().unsaturation).toBe(0); | ||
expect(new MF('CO3(--)').getInfo().unsaturation).toBe(3); | ||
expect(new MF('HO(-)').getInfo().unsaturation).toBe(1); | ||
expect(new MF('F(-)').getInfo().unsaturation).toBe(1); | ||
expect(new MF('Na+').getInfo().unsaturation).toBe(0); | ||
expect(new MF('NH4+').getInfo().unsaturation).toBe(-1); | ||
expect(new MF('CH4').getInfo().unsaturation).toBe(0); | ||
expect(new MF('C10H22O').getInfo().unsaturation).toBe(0); | ||
expect(new MF('H+').getInfo().unsaturation).toBe(0); | ||
expect(new MF('CO3(--)').getInfo().unsaturation).toBe(3); | ||
expect(new MF('HO(-)').getInfo().unsaturation).toBe(1); | ||
expect(new MF('F(-)').getInfo().unsaturation).toBe(1); | ||
expect(new MF('Na+').getInfo().unsaturation).toBe(0); | ||
expect(new MF('NH4+').getInfo().unsaturation).toBe(-1); | ||
}); | ||
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 | ||
} | ||
] | ||
]); | ||
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', | ||
unsaturation: 6, | ||
atoms: { C: 2, N: 6 } | ||
}); | ||
let info = mf.getInfo(); | ||
expect(info).toEqual({ | ||
monoisotopicMass: 111.01586865055, | ||
mass: 111.04112137534844, | ||
charge: 0, | ||
mf: 'C[13C]N4[15N]2', | ||
unsaturation: 6, | ||
atoms: { C: 2, N: 6 } | ||
}); | ||
var newMF = mf.toMF(); | ||
expect(newMF).toBe('C[13C]N4[15N]2'); | ||
var newMF = mf.toMF(); | ||
expect(newMF).toBe('C[13C]N4[15N]2'); | ||
}); | ||
test('MF of DNA HODampDtmpDcmpDgmpH ', () => { | ||
var mf = new MF('HODampDtmpDgmpDcmpH'); | ||
var info = mf.getInfo(); | ||
expect(info).toEqual({ mass: 1253.8043977028433, | ||
monoisotopicMass: 1253.21310019311, | ||
charge: 0, | ||
mf: 'C39H51N15O25P4', | ||
atoms: { C: 39, H: 51, N: 15, O: 25, P: 4 }, | ||
unsaturation: 24 }); | ||
var mf = new MF('HODampDtmpDgmpDcmpH'); | ||
var info = mf.getInfo(); | ||
expect(info).toEqual({ | ||
mass: 1253.8043977028433, | ||
monoisotopicMass: 1253.21310019311, | ||
charge: 0, | ||
mf: 'C39H51N15O25P4', | ||
atoms: { C: 39, H: 51, N: 15, O: 25, P: 4 }, | ||
unsaturation: 24 | ||
}); | ||
}); | ||
test('MF of RNA HOAmpUmpH ', () => { | ||
var mf = new MF('HOAmpUmpH'); | ||
var info = mf.getInfo(); | ||
expect(info).toEqual({ mass: 653.388021231099, | ||
monoisotopicMass: 653.08838712715, | ||
charge: 0, | ||
mf: 'C19H25N7O15P2', | ||
atoms: { C: 19, H: 25, N: 7, O: 15, P: 2 }, | ||
unsaturation: 12 }); | ||
var mf = new MF('HOAmpUmpH'); | ||
var info = mf.getInfo(); | ||
expect(info).toEqual({ | ||
mass: 653.388021231099, | ||
monoisotopicMass: 653.08838712715, | ||
charge: 0, | ||
mf: 'C19H25N7O15P2', | ||
atoms: { C: 19, H: 25, N: 7, O: 15, P: 2 }, | ||
unsaturation: 12 | ||
}); | ||
}); | ||
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 } | ||
] | ||
]); | ||
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 } | ||
] | ||
]); | ||
var newMF = mf.toMF(); | ||
expect(newMF).toBe('CC{50,50}H'); | ||
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', | ||
unsaturation: 2.5, | ||
atoms: { C: 2, H: 1 } | ||
}); | ||
let info = mf.getInfo(); | ||
expect(info).toEqual({ | ||
monoisotopicMass: 25.00782503223, | ||
mass: 25.520354068326025, | ||
charge: 0, | ||
mf: 'CC{50,50}H', | ||
unsaturation: 2.5, | ||
atoms: { C: 2, H: 1 } | ||
}); | ||
}); | ||
test('MF of H(+)(H+)-1H', () => { | ||
var mf = new MF('H(+)(H+)-1H'); | ||
var newMF = mf.toMF(); | ||
expect(newMF).toBe('H'); | ||
let info = mf.getInfo(); | ||
expect(info).toEqual({ | ||
atoms: { H: 1 }, | ||
charge: 0, | ||
mass: 1.0079407540557772, | ||
mf: 'H', | ||
monoisotopicMass: 1.00782503223, | ||
unsaturation: 0.5 | ||
}); | ||
}); |
@@ -5,3 +5,3 @@ 'use strict'; | ||
const elements = require('chemical-elements/src/elementsAndStableIsotopesObject.js'); | ||
const groups = require('chemical-elements/src/groupsObject.js'); | ||
const groups = require('chemical-groups/src/groupsObject.js'); | ||
const unsaturations = require('chemical-elements/src/unsaturationsObject.js'); | ||
@@ -20,144 +20,140 @@ const Kind = require('../Kind'); | ||
module.exports = function getInfo(parts, options = {}) { | ||
let { customUnsaturations = {} } = options; | ||
if (parts.length === 0) return {}; | ||
if (parts.length === 1) { | ||
return getProcessedPart(parts[0], customUnsaturations); | ||
} | ||
let { customUnsaturations = {} } = options; | ||
if (parts.length === 0) return {}; | ||
if (parts.length === 1) { | ||
return getProcessedPart(parts[0], customUnsaturations); | ||
} | ||
var result = { | ||
parts: [] | ||
}; | ||
for (let part of parts) { | ||
result.parts.push(getProcessedPart(part, customUnsaturations)); | ||
} | ||
var result = { | ||
parts: [] | ||
}; | ||
for (let part of parts) { | ||
result.parts.push(getProcessedPart(part, customUnsaturations)); | ||
} | ||
result.monoisotopicMass = 0; | ||
result.mass = 0; | ||
result.charge = 0; | ||
result.mf = result.parts.map((a) => a.mf).join('.'); | ||
result.parts.forEach((a) => { | ||
result.mass += a.mass; | ||
result.monoisotopicMass += a.monoisotopicMass; | ||
result.charge += a.charge; | ||
}); | ||
return result; | ||
result.monoisotopicMass = 0; | ||
result.mass = 0; | ||
result.charge = 0; | ||
result.mf = result.parts.map(a => a.mf).join('.'); | ||
result.parts.forEach(a => { | ||
result.mass += a.mass; | ||
result.monoisotopicMass += a.monoisotopicMass; | ||
result.charge += a.charge; | ||
}); | ||
return result; | ||
}; | ||
function getProcessedPart(part, customUnsaturations) { | ||
let currentPart = { | ||
mass: 0, | ||
monoisotopicMass: 0, | ||
charge: 0, | ||
mf: '', | ||
atoms: partToAtoms(part) | ||
}; | ||
let unsaturation = 0; | ||
let validUnsaturation = true; | ||
currentPart.mf = partToMF(part); | ||
for (let line of part) { | ||
let currentElement = ''; | ||
switch (line.kind) { | ||
case Kind.ATOM: { | ||
currentElement = line.value; | ||
let element = elements[line.value]; | ||
// todo should we have a kind GROUP ? | ||
if (!element) { | ||
element = groups[line.value]; | ||
if (!element) throw Error(`Unknown element: ${line.value}`); | ||
if (!customUnsaturations[line.value]) { | ||
customUnsaturations[line.value] = element.unsaturation; | ||
} | ||
} | ||
let currentPart = { | ||
mass: 0, | ||
monoisotopicMass: 0, | ||
charge: 0, | ||
mf: '', | ||
atoms: partToAtoms(part) | ||
}; | ||
let unsaturation = 0; | ||
let validUnsaturation = true; | ||
currentPart.mf = partToMF(part); | ||
for (let line of part) { | ||
let currentElement = ''; | ||
switch (line.kind) { | ||
case Kind.ATOM: { | ||
currentElement = line.value; | ||
let element = elements[line.value]; | ||
// todo should we have a kind GROUP ? | ||
if (!element) { | ||
element = groups[line.value]; | ||
if (!element) throw Error(`Unknown element: ${line.value}`); | ||
if (!customUnsaturations[line.value]) { | ||
customUnsaturations[line.value] = element.unsaturation; | ||
} | ||
} | ||
if (!element) throw new Error(`Unknown element: ${line.value}`); | ||
currentPart.monoisotopicMass += | ||
element.monoisotopicMass * line.multiplier; | ||
currentPart.mass += element.mass * line.multiplier; | ||
break; | ||
} | ||
case Kind.ISOTOPE: { | ||
currentElement = line.value.atom; | ||
let isotope = isotopes[line.value.isotope + line.value.atom]; | ||
if (!isotope) { | ||
throw new Error( | ||
`Unknown isotope: ${line.value.isotope}${ | ||
line.value.atom | ||
}` | ||
); | ||
} | ||
currentPart.monoisotopicMass += isotope.mass * line.multiplier; | ||
currentPart.mass += isotope.mass * line.multiplier; | ||
break; | ||
} | ||
case Kind.ISOTOPE_RATIO: { | ||
currentElement = line.value.atom; | ||
let isotopeRatioInfo = getIsotopeRatioInfo(line.value); | ||
currentPart.monoisotopicMass += | ||
isotopeRatioInfo.monoisotopicMass * line.multiplier; | ||
currentPart.mass += isotopeRatioInfo.mass * line.multiplier; | ||
break; | ||
} | ||
case Kind.CHARGE: | ||
currentPart.charge = line.value; | ||
if (validUnsaturation) { | ||
unsaturation -= line.value; | ||
} | ||
break; | ||
default: | ||
throw new Error('Unimplemented Kind in getInfo', line.kind); | ||
if (!element) throw new Error(`Unknown element: ${line.value}`); | ||
currentPart.monoisotopicMass += | ||
element.monoisotopicMass * line.multiplier; | ||
currentPart.mass += element.mass * line.multiplier; | ||
break; | ||
} | ||
case Kind.ISOTOPE: { | ||
currentElement = line.value.atom; | ||
let isotope = isotopes[line.value.isotope + line.value.atom]; | ||
if (!isotope) { | ||
throw new Error( | ||
`Unknown isotope: ${line.value.isotope}${line.value.atom}` | ||
); | ||
} | ||
if (currentElement) { | ||
if (customUnsaturations[currentElement] !== undefined) { | ||
unsaturation += | ||
customUnsaturations[currentElement] * line.multiplier; | ||
} else if (unsaturations[currentElement] !== undefined) { | ||
unsaturation += unsaturations[currentElement] * line.multiplier; | ||
} else { | ||
validUnsaturation = false; | ||
} | ||
currentPart.monoisotopicMass += isotope.mass * line.multiplier; | ||
currentPart.mass += isotope.mass * line.multiplier; | ||
break; | ||
} | ||
case Kind.ISOTOPE_RATIO: { | ||
currentElement = line.value.atom; | ||
let isotopeRatioInfo = getIsotopeRatioInfo(line.value); | ||
currentPart.monoisotopicMass += | ||
isotopeRatioInfo.monoisotopicMass * line.multiplier; | ||
currentPart.mass += isotopeRatioInfo.mass * line.multiplier; | ||
break; | ||
} | ||
case Kind.CHARGE: | ||
currentPart.charge = line.value; | ||
if (validUnsaturation) { | ||
unsaturation -= line.value; | ||
} | ||
break; | ||
default: | ||
throw new Error('Unimplemented Kind in getInfo', line.kind); | ||
} | ||
if (currentElement) { | ||
if (customUnsaturations[currentElement] !== undefined) { | ||
unsaturation += customUnsaturations[currentElement] * line.multiplier; | ||
} else if (unsaturations[currentElement] !== undefined) { | ||
unsaturation += unsaturations[currentElement] * line.multiplier; | ||
} else { | ||
validUnsaturation = false; | ||
} | ||
} | ||
} | ||
// need to calculate the observedMonoisotopicMass | ||
if (currentPart.charge) { | ||
currentPart.observedMonoisotopicMass = | ||
(currentPart.monoisotopicMass - | ||
currentPart.charge * ELECTRON_MASS) / | ||
Math.abs(currentPart.charge); | ||
} | ||
if (validUnsaturation) { | ||
currentPart.unsaturation = unsaturation / 2 + 1; | ||
} | ||
return currentPart; | ||
// need to calculate the observedMonoisotopicMass | ||
if (currentPart.charge) { | ||
currentPart.observedMonoisotopicMass = | ||
(currentPart.monoisotopicMass - currentPart.charge * ELECTRON_MASS) / | ||
Math.abs(currentPart.charge); | ||
} | ||
if (validUnsaturation) { | ||
currentPart.unsaturation = unsaturation / 2 + 1; | ||
} | ||
return currentPart; | ||
} | ||
function getIsotopeRatioInfo(value) { | ||
let result = { | ||
mass: 0, | ||
monoisotopicMass: 0 | ||
}; | ||
let element = elements[value.atom]; | ||
if (!element) throw new Error(`Element not found: ${value.atom}`); | ||
let isotopesArray = element.isotopes; | ||
let ratios = normalize(value.ratio); | ||
let max = Math.max(...ratios); | ||
if (ratios.length > isotopesArray.length) { | ||
throw new Error( | ||
`the number of specified ratios is bigger that the number of stable isotopes: ${ | ||
value.atom | ||
}` | ||
); | ||
let result = { | ||
mass: 0, | ||
monoisotopicMass: 0 | ||
}; | ||
let element = elements[value.atom]; | ||
if (!element) throw new Error(`Element not found: ${value.atom}`); | ||
let isotopesArray = element.isotopes; | ||
let ratios = normalize(value.ratio); | ||
let max = Math.max(...ratios); | ||
if (ratios.length > isotopesArray.length) { | ||
throw new Error( | ||
`the number of specified ratios is bigger that the number of stable isotopes: ${ | ||
value.atom | ||
}` | ||
); | ||
} | ||
for (let i = 0; i < ratios.length; i++) { | ||
result.mass += ratios[i] * isotopesArray[i].mass; | ||
if (max === ratios[i] && result.monoisotopicMass === 0) { | ||
result.monoisotopicMass = isotopesArray[i].mass; | ||
} | ||
for (let i = 0; i < ratios.length; i++) { | ||
result.mass += ratios[i] * isotopesArray[i].mass; | ||
if (max === ratios[i] && result.monoisotopicMass === 0) { | ||
result.monoisotopicMass = isotopesArray[i].mass; | ||
} | ||
} | ||
return result; | ||
} | ||
return result; | ||
} | ||
function normalize(array) { | ||
let sum = array.reduce((prev, current) => prev + current, 0); | ||
return array.map((a) => a / sum); | ||
let sum = array.reduce((prev, current) => prev + current, 0); | ||
return array.map(a => a / sum); | ||
} |
@@ -5,23 +5,34 @@ 'use strict'; | ||
module.exports = function partToMF(part) { | ||
var mf = []; | ||
for (let line of part) { | ||
switch (line.kind) { | ||
case Kind.ISOTOPE: | ||
mf.push(`[${line.value.isotope}${line.value.atom}]${(line.multiplier !== 1) ? line.multiplier : ''}`); | ||
break; | ||
case Kind.ISOTOPE_RATIO: | ||
mf.push(`${line.value.atom}{${line.value.ratio.join(',')}}${(line.multiplier !== 1) ? line.multiplier : ''}`); | ||
break; | ||
case Kind.ATOM: | ||
mf.push(line.value + ((line.multiplier !== 1) ? line.multiplier : '')); | ||
break; | ||
case Kind.CHARGE: | ||
mf.push(`(${(line.value > 0) ? `+${line.value}` : line.value})`); | ||
break; | ||
default: | ||
} | ||
var mf = []; | ||
for (let line of part) { | ||
switch (line.kind) { | ||
case Kind.ISOTOPE: | ||
if (line.multiplier !== 0) | ||
mf.push( | ||
`[${line.value.isotope}${line.value.atom}]${ | ||
line.multiplier !== 1 ? line.multiplier : '' | ||
}` | ||
); | ||
break; | ||
case Kind.ISOTOPE_RATIO: | ||
if (line.multiplier !== 0) | ||
mf.push( | ||
`${line.value.atom}{${line.value.ratio.join(',')}}${ | ||
line.multiplier !== 1 ? line.multiplier : '' | ||
}` | ||
); | ||
break; | ||
case Kind.ATOM: | ||
if (line.multiplier !== 0) | ||
mf.push(line.value + (line.multiplier !== 1 ? line.multiplier : '')); | ||
break; | ||
case Kind.CHARGE: | ||
if (line.value === 0) break; | ||
mf.push(`(${line.value > 0 ? `+${line.value}` : line.value})`); | ||
break; | ||
default: | ||
} | ||
return mf.join(''); | ||
} | ||
return mf.join(''); | ||
}; |
'use strict'; | ||
const Kind = require('../Kind'); | ||
const groups = require('chemical-elements/src/groupsObject.js'); | ||
const groups = require('chemical-groups/src/groupsObject.js'); | ||
const atomSorter = require('atom-sorter'); | ||
@@ -15,217 +15,213 @@ | ||
module.exports = function toParts(lines, options = {}) { | ||
const { expand: shouldExpandGroups = true } = options; | ||
let parts = []; | ||
const { expand: shouldExpandGroups = true } = options; | ||
let parts = []; | ||
let currentPart = createNewPart(); | ||
let previousKind = Kind.BEGIN; | ||
parts.push(currentPart); | ||
for (let line of lines) { | ||
switch (line.kind) { | ||
case Kind.OPENING_PARENTHESIS: | ||
openingParenthesis(currentPart); | ||
break; | ||
case Kind.CLOSING_PARENTHESIS: | ||
closingParenthesis(currentPart); | ||
break; | ||
case Kind.PRE_MULTIPLIER: | ||
preMultiplier(currentPart, line); | ||
break; | ||
case Kind.MULTIPLIER: | ||
postMultiplier(currentPart, line.value, previousKind); | ||
break; | ||
case Kind.SALT: | ||
globalPartMultiplier(currentPart); | ||
currentPart = createNewPart(); | ||
parts.push(currentPart); | ||
break; | ||
case Kind.ATOM: | ||
case Kind.ISOTOPE_RATIO: | ||
case Kind.ISOTOPE: | ||
case Kind.CHARGE: | ||
currentPart.lines.push( | ||
Object.assign({}, line, { multiplier: 1 }) | ||
); | ||
break; | ||
case Kind.COMMENT: // we ignore comments to create the parts and canonized MF | ||
break; | ||
case Kind.TEXT: | ||
break; | ||
default: | ||
throw new Error(`Can not process mf having: ${line.kind}`); | ||
} | ||
previousKind = line.kind; | ||
let currentPart = createNewPart(); | ||
let previousKind = Kind.BEGIN; | ||
parts.push(currentPart); | ||
for (let line of lines) { | ||
switch (line.kind) { | ||
case Kind.OPENING_PARENTHESIS: | ||
openingParenthesis(currentPart); | ||
break; | ||
case Kind.CLOSING_PARENTHESIS: | ||
closingParenthesis(currentPart); | ||
break; | ||
case Kind.PRE_MULTIPLIER: | ||
preMultiplier(currentPart, line); | ||
break; | ||
case Kind.MULTIPLIER: | ||
postMultiplier(currentPart, line.value, previousKind); | ||
break; | ||
case Kind.SALT: | ||
globalPartMultiplier(currentPart); | ||
currentPart = createNewPart(); | ||
parts.push(currentPart); | ||
break; | ||
case Kind.ATOM: | ||
case Kind.ISOTOPE_RATIO: | ||
case Kind.ISOTOPE: | ||
case Kind.CHARGE: | ||
currentPart.lines.push(Object.assign({}, line, { multiplier: 1 })); | ||
break; | ||
case Kind.COMMENT: // we ignore comments to create the parts and canonized MF | ||
break; | ||
case Kind.TEXT: | ||
break; | ||
default: | ||
throw new Error(`Can not process mf having: ${line.kind}`); | ||
} | ||
globalPartMultiplier(currentPart); | ||
if (shouldExpandGroups) expandGroups(parts); | ||
return combineAtomsIsotopesCharges(parts); | ||
previousKind = line.kind; | ||
} | ||
globalPartMultiplier(currentPart); | ||
if (shouldExpandGroups) expandGroups(parts); | ||
return combineAtomsIsotopesCharges(parts); | ||
}; | ||
function createNewPart() { | ||
let currentMultiplier = { value: 1, fromIndex: 0 }; | ||
return { | ||
lines: [], | ||
multipliers: [currentMultiplier], | ||
currentMultiplier | ||
}; | ||
let currentMultiplier = { value: 1, fromIndex: 0 }; | ||
return { | ||
lines: [], | ||
multipliers: [currentMultiplier], | ||
currentMultiplier | ||
}; | ||
} | ||
function openingParenthesis(currentPart) { | ||
currentPart.currentMultiplier = { | ||
value: 1, | ||
fromIndex: currentPart.lines.length | ||
}; | ||
currentPart.multipliers.push(currentPart.currentMultiplier); | ||
currentPart.currentMultiplier = { | ||
value: 1, | ||
fromIndex: currentPart.lines.length | ||
}; | ||
currentPart.multipliers.push(currentPart.currentMultiplier); | ||
} | ||
function closingParenthesis(currentPart) { | ||
currentPart.currentMultiplier = currentPart.multipliers.pop(); | ||
if (currentPart.currentMultiplier !== 1) { | ||
for ( | ||
let i = currentPart.currentMultiplier.fromIndex; | ||
i < currentPart.lines.length; | ||
i++ | ||
) { | ||
currentPart.lines[i].multiplier *= | ||
currentPart.currentMultiplier.value; | ||
} | ||
currentPart.currentMultiplier = currentPart.multipliers.pop(); | ||
if (currentPart.currentMultiplier !== 1) { | ||
for ( | ||
let i = currentPart.currentMultiplier.fromIndex; | ||
i < currentPart.lines.length; | ||
i++ | ||
) { | ||
currentPart.lines[i].multiplier *= currentPart.currentMultiplier.value; | ||
} | ||
} | ||
} | ||
function preMultiplier(currentPart, line) { | ||
currentPart.currentMultiplier.value *= line.value; | ||
currentPart.currentMultiplier.value *= line.value; | ||
} | ||
function globalPartMultiplier(currentPart) { | ||
for ( | ||
let i = currentPart.multipliers[0].fromIndex; | ||
i < currentPart.lines.length; | ||
i++ | ||
) { | ||
currentPart.lines[i].multiplier *= currentPart.multipliers[0].value; | ||
} | ||
for ( | ||
let i = currentPart.multipliers[0].fromIndex; | ||
i < currentPart.lines.length; | ||
i++ | ||
) { | ||
currentPart.lines[i].multiplier *= currentPart.multipliers[0].value; | ||
} | ||
} | ||
function postMultiplier(currentPart, value, previousKind) { | ||
if (previousKind === Kind.CLOSING_PARENTHESIS) { | ||
// need to apply to everything till the previous parenthesis | ||
for ( | ||
let i = currentPart.currentMultiplier.fromIndex; | ||
i < currentPart.lines.length; | ||
i++ | ||
) { | ||
currentPart.lines[i].multiplier *= value; | ||
} | ||
} else { | ||
// just applies to the previous element | ||
currentPart.lines[currentPart.lines.length - 1].multiplier *= value; | ||
if (previousKind === Kind.CLOSING_PARENTHESIS) { | ||
// need to apply to everything till the previous parenthesis | ||
for ( | ||
let i = currentPart.currentMultiplier.fromIndex; | ||
i < currentPart.lines.length; | ||
i++ | ||
) { | ||
currentPart.lines[i].multiplier *= value; | ||
} | ||
} else { | ||
// just applies to the previous element | ||
currentPart.lines[currentPart.lines.length - 1].multiplier *= value; | ||
} | ||
} | ||
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; | ||
} | ||
} | ||
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); | ||
} | ||
} | ||
if (expanded) part.lines = part.lines.filter(a => a); | ||
} | ||
} | ||
function combineAtomsIsotopesCharges(parts) { | ||
let results = []; | ||
for (let part of parts) { | ||
let result = []; | ||
results.push(result); | ||
calculateAndSortKeys(part); | ||
let results = []; | ||
for (let part of parts) { | ||
let result = []; | ||
results.push(result); | ||
calculateAndSortKeys(part); | ||
let currentKey = ''; | ||
for (let key of part.keys) { | ||
if (key.key === Kind.CHARGE) { | ||
if (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); | ||
} else { | ||
result[result.length - 1].multiplier += | ||
key.value.multiplier; | ||
} | ||
} | ||
currentKey = key.key; | ||
let currentKey = ''; | ||
for (let key of part.keys) { | ||
if (key.key === Kind.CHARGE) { | ||
if (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); | ||
} else { | ||
result[result.length - 1].multiplier += key.value.multiplier; | ||
} | ||
} | ||
currentKey = key.key; | ||
} | ||
result.sort((a, b) => { | ||
if (a.kind === Kind.CHARGE) return 1; | ||
if (b.kind === Kind.CHARGE) return -1; | ||
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; | ||
}); | ||
} | ||
return results; | ||
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; | ||
}); | ||
} | ||
return results; | ||
} | ||
function calculateAndSortKeys(part) { | ||
part.keys = []; | ||
for (var line of part.lines) { | ||
part.keys.push({ | ||
key: getKey(line), | ||
value: line | ||
}); | ||
} | ||
part.keys.sort((a, b) => stringComparator(a.key, b.key)); | ||
part.keys = []; | ||
for (var line of part.lines) { | ||
part.keys.push({ | ||
key: getKey(line), | ||
value: line | ||
}); | ||
} | ||
part.keys.sort((a, b) => stringComparator(a.key, b.key)); | ||
} | ||
function getKey(line) { | ||
let key = [line.kind]; | ||
let key = [line.kind]; | ||
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]); | ||
} | ||
} | ||
} | ||
return key.join('-'); | ||
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]); | ||
} | ||
} | ||
} | ||
return key.join('-'); | ||
} | ||
function stringComparator(a, b) { | ||
if (a < b) return -1; | ||
if (a > b) return 1; | ||
return 0; | ||
if (a < b) return -1; | ||
if (a > b) return 1; | ||
return 0; | ||
} |
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
1477
53647
4
+ Addedchemical-groups@^0.6.4
- Removedmf-parser@0.6.4(transitive)
Updatedatom-sorter@^0.6.4
Updatedchemical-elements@^0.6.4
Updatedmf-utilities@^0.6.4