-
-
Save WoolDoughnut310/610561a96033449e28dd54323965ece0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const balanceEquation = ( | |
reactants, | |
products | |
) => { | |
let balancingNumbers = [ | |
new Array(reactants.length).fill(1), | |
new Array(products.length).fill(1), | |
]; | |
let reactantAtoms = {}; | |
let productAtoms = {}; | |
let reactantElements = reactants.map(Object.keys); | |
let productElements = products.map(Object.keys); | |
let elements = new Set(); | |
let reactantElementCount = {}; | |
let productElementCount = {}; | |
let uniqueElements = []; | |
const balanceCompound = (i, side, element) => { | |
const compound = (side === 0 ? reactants : products)[i]; | |
const sideAtoms = side === 0 ? reactantAtoms : productAtoms; | |
const otherAtoms = side === 0 ? productAtoms : reactantAtoms; | |
let oldBalancingNumber = balancingNumbers[side][i]; | |
let balancingNumber = oldBalancingNumber; | |
// The amount of atoms on the side, excluding from the current compound | |
const existingAtoms = | |
sideAtoms[element] - balancingNumber * compound[element]; | |
balancingNumber = | |
(otherAtoms[element] - existingAtoms) / compound[element]; | |
sideAtoms[element] = | |
existingAtoms + balancingNumber * compound[element]; | |
// Update the amount of atoms for every other element in the compound | |
for (let otherElement of Object.keys(compound)) { | |
if (otherElement === element) continue; | |
const otherExistingAtoms = | |
sideAtoms[otherElement] - | |
oldBalancingNumber * compound[otherElement]; | |
sideAtoms[otherElement] = | |
otherExistingAtoms + balancingNumber * compound[otherElement]; | |
} | |
balancingNumbers[side][i] = balancingNumber; | |
// If balancing number is a decimal number | |
if ((parseFloat(balancingNumber) | 0) !== balancingNumber) { | |
// Scale to a whole number | |
const scale = lcm(balancingNumber, 1) / balancingNumber; | |
// i goes from 0 to 1, for reactants to products | |
for (let i = 0; i < balancingNumbers.length; i++) { | |
const sideBalancingNumbers = balancingNumbers[i]; | |
// j goes up to the number of compounds from each side | |
for (let j = 0; j < sideBalancingNumbers.length; j++) { | |
balancingNumbers[i][j] *= scale; | |
} | |
} | |
// Can't forget to update the number of atoms | |
elements.forEach((element) => { | |
reactantAtoms[element] *= scale; | |
productAtoms[element] *= scale; | |
}); | |
} | |
// Update the balancing numbers on any compounds that | |
// would have been affected because they contained a unique element | |
if (uniqueElements.length > 0) { | |
for (let key of Object.keys(compound)) { | |
if (element === key) continue; | |
if (!uniqueElements.includes(key)) return; | |
const otherElements = | |
side === 0 ? productElements : reactantElements; | |
for (let i = 0; i < otherElements.length; i++) { | |
const compoundElement = otherElements[i]; | |
if (!compoundElement.includes(key)) continue; | |
// if side is 0 (bool false), balance on side 1 (products), 0 otherwise | |
balanceCompound(i, !side ? 1 : 0, key); | |
} | |
} | |
} | |
}; | |
// Couting the amount of atoms of an element on each side | |
for (let reactant of reactants) { | |
for (let [element, value] of Object.entries(reactant)) { | |
elements.add(element); | |
if (!reactantAtoms[element]) reactantAtoms[element] = 0; | |
reactantAtoms[element] += value; | |
} | |
} | |
for (let product of products) { | |
for (let [element, value] of Object.entries(product)) { | |
if (!productAtoms[element]) productAtoms[element] = 0; | |
productAtoms[element] += value; | |
} | |
} | |
for (let compoundElements of reactantElements) { | |
for (let element of compoundElements) { | |
if (!reactantElementCount[element]) | |
reactantElementCount[element] = 0; | |
reactantElementCount[element]++; | |
} | |
} | |
for (let compoundElements of productElements) { | |
for (let element of compoundElements) { | |
if (!productElementCount[element]) productElementCount[element] = 0; | |
productElementCount[element]++; | |
} | |
} | |
for (let i = 0; i < reactantElements.length; i++) { | |
const reactantCompoundElements = reactantElements[i]; | |
for (let element of reactantCompoundElements) { | |
if ( | |
!( | |
reactantElementCount[element] === 1 && | |
productElementCount[element] === 1 | |
) | |
) | |
continue; | |
uniqueElements.push(element); | |
for (let j = 0; j < productElements.length; j++) { | |
const productCompoundElements = productElements[j]; | |
if (!productCompoundElements.includes(element)) continue; | |
/* Balance the compound on the side with less | |
atoms and a lower existing coefficient */ | |
if ( | |
reactants[i][element] <= products[j][element] && | |
balancingNumbers[0][i] <= balancingNumbers[1][j] | |
) { | |
// We'll look at this function later | |
balanceCompound(i, 0, element); | |
} else { | |
balanceCompound(j, 1, element); | |
} | |
} | |
} | |
} | |
elements.forEach((element) => { | |
if (uniqueElements.includes(element)) return; | |
let smallestCandidate = { | |
side: 0, | |
balancingNumber: Infinity, | |
value: Infinity, | |
numElements: Infinity, | |
index: 0, | |
}; | |
// Find the compound with the smallest number of elements | |
for (let i = 0; i < reactants.length; i++) { | |
const reactant = reactants[i]; | |
const balancingNumber = balancingNumbers[0][i]; | |
const numElements = reactantElements[i].length; | |
if (!reactantElements[i].includes(element)) continue; | |
if (numElements >= smallestCandidate.numElements) continue; | |
smallestCandidate = { | |
side: 0, | |
balancingNumber, | |
value: reactant[element], | |
numElements, | |
index: i, | |
}; | |
} | |
for (let i = 0; i < products.length; i++) { | |
const product = products[i]; | |
const balancingNumber = balancingNumbers[1][i]; | |
const numElements = productElements[i].length; | |
if (!productElements[i].includes(element)) continue; | |
if (numElements >= smallestCandidate.numElements) continue; | |
smallestCandidate = { | |
side: 1, | |
balancingNumber, | |
value: product[element], | |
numElements, | |
index: i, | |
}; | |
} | |
balanceCompound( | |
smallestCandidate.index, | |
smallestCandidate.side, | |
element | |
); | |
}); | |
return balancingNumbers; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment