Regex splitting on parenthesis, first uppercase, and digits - javascript
Right now, I have the user inputting a chemical formula, say Cu(NO3)2, and splitting the elements and numbers into an array. This code works for all formulas except for those with parentheses. (which actually came from a user-submitted thread on here.)
var userArray=userIn.replace(/\d+/g, '~$&').split(/(?=[A-Z])|~/);
The reason I used replace then split was to ensure that if someone entered H12, it would come out as H , 12 ... rather than H , 1 , 2.
I'm getting Cu( , N , O , 3) , 2 ... when in fact I want... Cu , ( , N , O , 3 , ) , 2
What about using match instead with the g flag:
var userArray=userIn.match(/(?:[A-Z][a-z]*|\d+|[()])/g);
You need to add the parentheses into your replace pattern, like so:
var userArray=userIn.replace(/\d+|\(|\)/g, '~$&').split(/(?=[A-Z])|~/);
Not a regex, more of a parser that can check the formulae, returns an array like you have specified or false if an error is discovered. This is pretty basic but you can easily add/change the rules (I'm no chemist), also contains the Periodic Table that it checks symbols against. Anyway, you may find it useful.
Javascript
var elements = [
["89", "Ac", "Actinium", "227"],
["13", "Al", "Aluminum", "26.98154"],
["56", "Ba", "Barium", "137.33"],
["4", "Be", "Beryllium", "9.01218"],
["83", "Bi", "Bismuth", "208.9804"],
["107", "Bh", "Bohrium", "262"],
["48", "Cd", "Cadmium", "112.4"],
["20", "Ca", "Calcium", "40.08"],
["55", "Cs", "Cesium", "132.9054"],
["24", "Cr", "Chromium", "51.996"],
["27", "Co", "Cobalt", "58.9332"],
["29", "Cu", "Copper", "63.546"],
["110", "Ds", "Darmstadtium", "261.9"],
["105", "Db", "Dubnium", "261.9"],
["87", "Fr", "Francium", "(233)"],
["31", "Ga", "Gallium", "69.72"],
["79", "Au", "Gold", "196.9655"],
["72", "Hf", "Hafnium", "178.49"],
["108", "Hs", "Hassium", "264.8"],
["49", "In", "Indium", "114.82"],
["77", "Ir", "Iridium", "192.2"],
["26", "Fe", "Iron", "55.85"],
["57", "La", "Lanthanum", "138.91"],
["82", "Pb", "Lead", "207.2"],
["3", "Li", "Lithium", "6.941"],
["12", "Mg", "Magnesium", "24.305"],
["25", "Mn", "Manganese", "54.9380"],
["109", "Mt", "Meitnerium", "265.9"],
["80", "Hg", "Mercury", "200.59"],
["42", "Mo", "Molybdenum", "95.94"],
["28", "Ni", "Nickel", "58.71"],
["41", "Nb", "Niobium", "92.91"],
["76", "Os", "Osmium", "190.2"],
["46", "Pd", "Palladium", "106.42"],
["78", "Pt", "Platinum", "195.09"],
["19", "K", "Potassium", "39.0983"],
["88", "Ra", "Radium", "226.0254"],
["75", "Re", "Rhenium", "186.23"],
["45", "Rh", "Rhodium", "102.91"],
["37", "Rb", "Rubidium", "85.4678"],
["44", "Ru", "Ruthenium", "101.1"],
["104", "Rf", "Rutherfordium", "260.9"],
["21", "Sc", "Scandium", "44.9559"],
["106", "Sg", "Seaborgium", "262.94"],
["47", "Ag", "Silver", "107.87"],
["11", "Na", "Sodium", "22.98977"],
["38", "Sr", "Strontium", "87.62"],
["73", "Ta", "Tantalum", "180.95"],
["43", "Tc", "Technetium", "(99)"],
["81", "Tl", "Thallium", "204.383"],
["50", "Sn", "Tin", "118.69"],
["22", "Ti", "Titanium", "47.90"],
["74", "W", "Tungsten", "183.85"],
["112", "Uub", "Ununbium", "276.8"],
["116", "Uuh", "Ununhexium", ""],
["114", "Uuq", "Ununquadium", "289"],
["23", "V", "Vanadium", "50.9414"],
["39", "Y", "Yttrium", "88.9059"],
["30", "Zn", "Zinc", "65.37"],
["40", "Zr", "Zirconium", "91.22"],
["51", "Sb", "Antimony", "121.75"],
["33", "As", "Arsenic", "74.9216"],
["85", "At", "Astatine", "(210)"],
["5", "B", "Boron", "10.81"],
["32", "Ge", "Germanium", "72.59"],
["84", "Po", "Polonium", "(210)"],
["14", "Si", "Silicon", "28.0855"],
["52", "Te", "Tellurium", "127.6"],
["35", "Br", "Bromine", "79.904"],
["6", "C", "Carbon", "12.011"],
["17", "Cl", "Chlorine", "35.453"],
["9", "F", "Fluorine", "18.998403"],
["1", "H", "Hydrogen", "1.007825"],
["53", "I", "Iodine", "126.9045"],
["7", "N", "Nitrogen", "14.0067"],
["8", "O", "Oxygen", "15.999"],
["15", "P", "Phosphorus", ""],
["34", "Se", "Selenium", "78.96"],
["16", "S", "Sulphur", "32.06"],
["18", "Ar", "Argon", "39.948"],
["2", "He", "Helium", "4.00260"],
["36", "Kr", "Krypton", "83.80"],
["10", "Ne", "Neon", "20.179"],
["86", "Rn", "Radon", "(222)"],
["54", "Xe", "Xenon", "131.29"],
["95", "Am", "Americium", "(243)"],
["97", "Bk", "Berkelium", "(247)"],
["98", "Cf", "Californium", "(251)"],
["58", "Ce", "Cerium", "140.12"],
["96", "Cm", "Curium", "(247)"],
["66", "Dy", "Dysprosium", "162.50"],
["99", "Es", "Einsteinium", "254"],
["68", "Er", "Erbium", "167.26"],
["63", "Eu", "Europium", "167.26"],
["100", "Fm", "Fermium", "(257)"],
["64", "Gd", "Gadolinium", "157.25"],
["67", "Ho", "Holmium", "164.9"],
["103", "Lr", "Lawrencium", "(262)"],
["71", "Lu", "Lutetium", "174.97"],
["101", "Md", "Mendelevium", "(258)"],
["60", "Nd", "Neodymium", ""],
["93", "Np", "Neptunium", "(237)"],
["102", "No", "Nobelium", "259"],
["94", "Pu", "Plutonium", "(244)"],
["59", "Pr", "Praseodymium", "140.91"],
["61", "Pm", "Promethium", "(147)"],
["91", "Pa", "Protactinium", "231.0359"],
["62", "Sm", "Samarium", "150.35"],
["65", "Tb", "Terbium", "158.92534"],
["90", "Th", "Thorium", "232.04"],
["69", "Tm", "Thulium", "168.93"],
["92", "U", "Uranium", "238.03"],
["70", "Yb", "Ytterbium", "173.04"]
];
var symbols = (function (el) {
var length = el.length,
i = 0,
result = [];
while (i < length) {
result.push(el[i][2]);
i += 1;
}
return result;
}(elements));
function splitFormula(string) {
var length = string.length,
symbolsLength = symbols.length,
result = [],
index,
chars,
symbol;
while (string) {
temp = string.charAt(0).trim();
if (!temp) {
string = string.slice(1);
} else if ("()+-*/".indexOf(temp) !== -1) {
result.push(temp);
string = string.slice(1);
} else if (typeof (+temp) === "number" && isFinite(temp)) {
temp = parseInt(string).toString();
result.push(temp);
string = string.slice(temp.length);
} else {
chars = 3;
while (chars) {
temp = string.slice(0, chars);
index = 0;
while (index < symbolsLength) {
if (temp === symbols[index]) {
result.push(temp);
string = string.slice(chars);
break;
}
index += 1;
}
if (index !== symbolsLength) {
break;
}
chars -= 1;
}
if (!chars) {
return false;
}
}
}
return result;
}
console.log(splitFormula("Cu(NO3)2 + H2O - H12"));
console.log(splitFormula("Cu(NO3)2 + H2O - X"));
Output
["Cu", "(", "N", "O", "3", ")", "2", "+", "H", "2", "O", "-", "H", "12"]
false
On jsfiddle
Update: you could even combine a parenthesis check, as found in this answer.
Related
Json array prints out as a string? Not array? [duplicate]
This question already has answers here: How to compute the sum and average of elements in an array? [duplicate] (35 answers) Closed 1 year ago. I'm currently getting my data from an API. It stores the weight log of animal checkups as shown below. Currently I'm storing it in a html tag: <h3 className='dogWeight'>Average: {item.weight}%</h3> But it prints the numbers together as a string (i.e 100102103). Is there anyway I can add all numbers in the array to just show the Average? { "dog":[ { "weight":[ "78", "100", "92", "86", "89", "88", "91", "87" ], "id":"1", }, { "weight":[ "75", "89", "95", "93", "99", "82", "89", "76" ], "id":"2", },
You can loop over data.dog and reduce the weight values by accumulating the parsed integer weight values and dividing by the length of the weight array. const data = { "dog": [{ "id": "1", "weight": [ "78", "100", "92", "86", "89", "88", "91", "87" ], }, { "id": "2", "weight": [ "75", "89", "95", "93", "99", "82", "89", "76" ], }] }; const avg = vals => vals.reduce((sum, x) => sum + parseInt(x, 10), 0) / vals.length; data.dog.forEach(({ id, weight }) => console.log(`ID: ${id}, Avg Weight: ${avg(weight).toFixed(2)}%`)); .as-console-wrapper { top: 0; max-heightL 100% !important; }
how to transform array of object using reduce of map?
I am trying to transform array of object to different format.I also take reference from this url Group js objects by multiple properties But not able to solve the problem I have a input array of object which have class and sections I need to transform or filter student in different format on basis of class and section. here is my code https://jsbin.com/nidudisuza/edit?js,output let expectedout = [ { class: 1, sections: [{ "section": "B", students: [ { "name": "Test", "class": 1, "gender": "M", "section": "B", "rollNumber": "123111", "sports": [ "Badminton", "Chess" ], "age": 7 }] }] }, { class: 3, sections: [{ "section": "B", students: [{ "name": "Rahul", "class": 3, "gender": "M", "section": "B", "rollNumber": "1231", "sports": [ "Badminton", "Chess" ], "age": 7 }] }] }, { class: 5, sections: [{ "section": "C", students: [ { "name": "Rajat", "class": 5, "gender": "M", "section": "C", "rollNumber": "123122", "sports": [ "Chess" ], "age": 9 } ] }] } ] const input = [{ "name": "Rahul", "class": 3, "gender": "M", "section": "B", "rollNumber": "1231", "sports": [ "Badminton", "Chess" ], "age": 7 }, { "name": "Rajat", "class": 5, "gender": "M", "section": "C", "rollNumber": "123122", "sports": [ "Chess" ], "age": 9 }, { "name": "Test", "class": 1, "gender": "M", "section": "B", "rollNumber": "123111", "sports": [ "Badminton", "Chess" ], "age": 7 }, ] function abc() { // let output = // data.map((i) => { // let obj = {}; // obj.class = i.class; // obj.sections = []; // obj.sections.push({ // section:obj.section, // students:[obj] // }) // return obj; // }) let output =input.reduce((acc,i)=>{ let cls = i.class; const found = acc.some(el => el.class === cls); let obj = { section:i.section, students:[] } found.sections.students.push[i] },[]) return output } console.log(abc()) }
I'd go for a 2 step process: Reorganize the values to be mapped by an index (both for class and sections) Then flatten out everything in arrays; depending on what you're doing having a dictionary could also be convenient. Below you can find the function that makes the dictionary, in the snippet there is also the second step that transforms the dictionary values in arrays. function abc() { let output = input.reduce((ac, x) => { let keyCl = x.class let keySec = x.section let orgClass = ac[keyCl] let sections = orgClass && orgClass.sections || {} let oldSec = sections && sections[keySec] let sectionBase = oldSec || { section: keySec, students: [] } sectionBase.students = [...(oldSec ? .students || []), x] return { ...ac, // we organize classes to be indexed in an obj, // we can flat this out later [keyCl]: { class: keyCl, sections: { ...sections, // like for classes we organize sections by key [keySec]: sectionBase } } } }, {}) return output } let expectedout = [{ class: 1, sections: [{ "section": "B", students: [{ "name": "Test", "class": 1, "gender": "M", "section": "B", "rollNumber": "123111", "sports": [ "Badminton", "Chess" ], "age": 7 }] }] }, { class: 3, sections: [{ "section": "B", students: [{ "name": "Rahul", "class": 3, "gender": "M", "section": "B", "rollNumber": "1231", "sports": [ "Badminton", "Chess" ], "age": 7 }] }] }, { class: 5, sections: [{ "section": "C", students: [ { "name": "Rajat", "class": 5, "gender": "M", "section": "C", "rollNumber": "123122", "sports": [ "Chess" ], "age": 9 } ] }] } ] const input = [{ "name": "Rahul", "class": 3, "gender": "M", "section": "B", "rollNumber": "1231", "sports": [ "Badminton", "Chess" ], "age": 7 }, { "name": "Rajat", "class": 5, "gender": "M", "section": "C", "rollNumber": "123122", "sports": [ "Chess" ], "age": 9 }, { "name": "Test", "class": 1, "gender": "M", "section": "B", "rollNumber": "123111", "sports": [ "Badminton", "Chess" ], "age": 7 }, ] function abc() { let output = input.reduce((ac, x) => { let keyCl = x.class let keySec = x.section let orgClass = ac[keyCl] let sections = orgClass && orgClass.sections || {} let oldSec = sections && sections[keySec] let sectionBase = oldSec || { section: keySec, students: [] } sectionBase.students = [...(oldSec && oldSec.students || []), x] return { ...ac, // we organize classes to be indexed in an obj, // we can flat this out later [keyCl]: { class: keyCl, sections: { ...sections, // like for classes we organize sections by key [keySec]: sectionBase } } } }, {}) return output } let res = abc() console.log('with keys', res) let onlyValues = Object.values(res).map(x => ({ ...x, sections: Object.values(x.sections) })) console.log('only values', onlyValues)
Using Array#reduce to build an Object with the collected data. Foreach object look in the accumulated object if there exists an prperty for this class. If not create one and add to this the groundstructure for this class. Afterwards add in both cases to the students array a new entry for this student. After creating by this the object use Object#entries to take only the values of the object to get the ished array. function modify(data) { let res= Object.values(data.reduce((acc, cur) => { if (!acc[cur.class]) { acc[cur.class] = { class: cur.class, sections: [{ "section": cur.section, students: [] }] }; } acc[cur.class].sections[0].students.push(cur); return acc; },{})); return res; } const data = [{ "name": "Rahul", "class": 3, "gender": "M", "section": "B", "rollNumber": "1231", "sports": [ "Badminton", "Chess" ], "age": 7 }, { "name": "Rajat", "class": 5, "gender": "M", "section": "C", "rollNumber": "123122", "sports": [ "Chess" ], "age": 9 }, { "name": "Test", "class": 1, "gender": "M", "section": "B", "rollNumber": "123111", "sports": [ "Badminton", "Chess" ], "age": 7 }, ] console.log(modify(data));
Angular nested filters by checkbox
I'm trying to build a small schedule app, which displays events happening on one of two days. Users are currently able to filter the category/topic of the events by checkboxes. Here is a demo: http://jsfiddle.net/qx3cD/201/ I want to create a nested filter, that first allows users to choose either Day 1 or Day 2, and then filters through those results by category. Is there a way to perform a nested filter with Angular? JS function MyCtrl($scope) { $scope.showAll = true; $scope.checkChange = function() { for(t in $scope.categoryArray){ if($scope.categoryArray[t].on){ $scope.showAll = false; return; } } $scope.showAll = true; }; $scope.myFunc = function(a) { if($scope.showAll) { return true; } var sel = false; for(cat in $scope.categoryArray){ var t = $scope.categoryArray[cat]; console.log(t); if(t.on){ if(a.category.indexOf(t.name) == -1){ return false; }else{ sel = true; } } } return sel; }; $scope.categoryArray = [{ name: "A", on: false}, {name:"B", on: false}, {name:"C", on: false}, {name:"D", on: false}, {name:"E", on: false}, {name:"F", on: false}, {name:"G", on: false}, {name:"H", on: false}]; $scope.sessionArray = [{ "time": "9:00", "day": "day 1", "name": "Session One", "category": ["A", "B", "E", "D"] }, { "time": "10:00", "day": "day 1", "name": "Session Two", "category": ["A", "B", "C", "D"] }, { "time": "11:00", "day": "day 1", "name": "Session Three", "category": ["G", "F", "D", "E"] }, { "time": "12:00", "day": "day 1", "name": "Intermission A", "category": ["A", "B", "C", "D"] }, { "time": "13:00", "day": "day 1", "name": "Session Four", "category": ["H", "A", "E"] }, { "time": "9:00", "day": "day 2", "name": "Session Five", "category": ["D", "E", "B", "G"] }, { "time": "11:00", "day": "day 2", "name": "Session Six", "category": ["A", "E", "C"] }, { "time": "12:00", "day": "day 2", "name": "Session Seven", "category": ["G", "H", "B", "C"] }] HTML <li ng-repeat="cat in categoryArray"> <label> <input type="checkbox" ng-model="cat.on" ng-change="checkChange()" />{{cat.name}}</label> </li> <hr> <h1><strong>Category:</strong></h1> <div ng-repeat="sessionItem in sessionArray | filter:myFunc | orderBy: 'id'" class="ng-scope"> <h3>{{sessionItem.name}}</h3> </div> DEMO
To nest another filter just add another pipe | and use the "filter" filter normally. <div ng-repeat="sessionItem in sessionArray | filter:myFunc | filter:theFilter | orderBy: 'id'"> Check the EXAMPLE here.
Filtering Array within a store record
I have a JSON object in a store that looks like the below. I am dynamically filtering values based on selections in a property grid. I am not filtering on the top level values (personName, primaryRole etc) but instead I want to drill down into the array of periods I have. For instance, I want to filter each record in the store for "periods" > "periodName". I'm not sure how to do this, as it's not something I have come across before. Using combo.getStore().filter('periodName', gridvalues.periodName); does not work as it doesn't find the periodName, and I cannot simply use periods.periodName unfortunately. "id": "531b0633-c9f5-4cbe-be69-5df4944b6e9b", "personName": "Ben", "primaryRole": "SWE", "secondaryRole": "SWE", "periods": [ { "personName": "Ben", "periodName": "Week 24", "teamName": "Tango", "experience": 2, "periodNumber": 24, "id": "71d514c2-a39f-40b7-af9e-51fbf30b2a48" }, { "personName": "Ben", "periodName": "Week 25", "teamName": "-", "experience": "-", "periodNumber": 25, "id": "49505fb2-97df-4f45-a11d-bb22c110a422" }, { "personName": "Ben", "periodName": "Week 26", "teamName": "-", "experience": "-", "periodNumber": 26, "id": "97f2b272-c23c-4320-a1ce-09dadf0c6b5d" }, "id": "7037ad16-dcbe-4326-8547-65108c03c874", "personName": "Luke", "primaryRole": "Product Owner", "secondaryRole": "SWE", "periods": [ { "personName": "Luke", "periodName": "Week 24", "teamName": "Tango", "experience": 3, "periodNumber": 24, "id": "e3184bef-c330-4219-ae21-8b65efcde77c" }, { "personName": "Luke", "periodName": "Week 25", "teamName": "-", "experience": "-", "periodNumber": 25, "id": "f431af7b-8060-40d2-8464-af533d943363" }, This is a record from my store: id: "531b0633-c9f5-4cbe-be69-5df4944b6e9b"periodNumber: 0periods: Array[16]0: Objectexperience: 2id: "71d514c2-a39f-40b7-af9e-51fbf30b2a48"periodName: "Week 24"periodNumber: 24personName: "Ben"teamName: "Tango"__proto__: Object1: Object2: Object3: Object4: Object5: Object6: Object7: Object8: Object9: Object10: Object11: Object12: Object13: Object14: Object15: Objectlength: 16__proto__: Array[0]personName: "Ben"primaryRole: "SWE"secondaryRole: "SWE"
Use the addFilter function in your controller: combo.getStore().addFilter({property: 'yourProperty', value: 'valueToFilter'}); For more info: http://docs.sencha.com/extjs/4.2.3/#!/api/Ext.data.Store-method-addFilter
How to repeat a function with Javascript
This is the code that I've been working on, which makes the background color flicker colors. I'm wondering if anyone knows how to make this repeat so that the background continues to change colors on and on and on. var a = new Array("ff", "ee", "dd", "cc", "bb", "aa", "99", "88", "77", "66", "55", "44", "33", "22", "11", "00", "00", "11", "22", "33", "44", "55", "66", "77", "88", "99", "AA", "BB", "CC", "DD", "EE", "ff"); x = 0; var b = new Array("ff", "ee", "dd", "cc", "bb", "aa", "99", "88", "77", "66", "55", "44", "33", "22", "11", "00", "00", "11", "22", "33", "44", "55", "66", "77", "88", "99", "AA", "BB", "CC", "DD", "EE", "ff"); x = 0; var c = new Array("00", "11", "22", "33", "44", "55", "66", "77", "88", "99", "AA", "BB", "CC", "DD", "EE", "ff", "ff", "ee", "dd", "cc", "bb", "aa", "99", "88", "77", "66", "55", "44", "33", "22", "11", "00"); x = 0; function bg_eff() { col_val = "#" + a[x] + b[x] + c[x]; document.bgColor = col_val; x++; if (x == 32) { clearInterval(change_bg); } } change_bg = setInterval("bg_eff()", 50);
x = (x + 1) % 32; Also, you should remove the clearInterval (and associated if), and there is no need to use a string for the setInterval: change_bg = setInterval(bg_eff, 50);
modified code here (using jquery) http://jsfiddle.net/generalhenry/S8g6k/1/ I use a recursive setTimeout instead of the interval, it's more resilient that way (if your function takes longer than the interval nothing odd occurs)
I would do this: x += 1; if ( x === 32 ) { x = 0; }
in addition to Matthew's answer but since the arrays are in the same sequence, you could do something like this. var a = new Array("ff", "ee", "dd", "cc", "bb", "aa", "99", "88", "77", "66", "55", "44", "33", "22", "11", "00", "00", "11", "22", "33", "44", "55","66", "77", "88", "99", "AA", "BB", "CC", "DD", "EE", "ff"); // one array var x = 0; // var for not global (even though in this context it still is...) function big_eff() { col_val = "#" + a[x] + a[(x + 5) % 32] + a[(x + 10) % 32]; // or whatever spacing you want document.bgColor = col_val; x = (x + 1) % 32; setTimeout("big_eff()",50); // setTimeout baby! }
a new version with pure Jquery http://jsfiddle.net/generalhenry/S8g6k/5/ I use .animate for much cleaner code (no need for the arrays or the x++) oh and warning: scary color swaping $("body").css("background-color","#ffff00"); var bg_eff; (bg_eff = function(x) { var duration = 1600; if(x) { $("body").animate({backgroundColor:"#0000ff"},duration,function(){ bg_eff(false); }); } else { $("body").animate({backgroundColor:"#ffff00"},duration,function(){ bg_eff(true); }); } })(true);