I have three inputs and three variables, my goal is to change variables values with the values inside the inputs
const inputs = [
document.querySelector(".bill-input"),
document.querySelector(".custom"),
document.querySelector(".people-number"),
];
var bill = 0;
var tip = 0;
var people = 0;
i accomplished to do it this way
inputs[0].addEventListener("keyup", (e) => {
bill = Number(e.target.value);
});
inputs[1].addEventListener("keyup", (e) => {
tip = Number(e.target.value);
});
inputs[2].addEventListener("keyup", (e) => {
people = Number(e.target.value);
});
I'm pretty sure this is not the optimal way to do it, so i wanted to ask if there's a way to do it with forEach or any other method that does not require for me to write every single one each time.
Add a data attribute to each input.
Use an object to maintain the state of those inputs instead of n variables.
Have one handler that can update the object properties based on their id.
// Initialise the values object
const values = { bill: 0, tip: 0, people: 0 };
// Cache the inputs, and add listeners to them
const inputs = document.querySelectorAll('input');
inputs.forEach(input => input.addEventListener('keyup', handleChange));
// Grab the id from the input's dataset, and
// set the values object property to match
// the input value
function handleChange() {
const { id } = this.dataset;
values[id] = this.value;
console.log(JSON.stringify(values));
}
input { display: block; }
Bill<input data-id="bill">
Tip <input data-id="tip">
People <input data-id="people">
Additional documentation
Destructuring assignment
Yes, you can use forEach. I used a switch to get the index of the input element (in inputs const) to know what variable update.
Please see snippet below :
var bill = 0;
var tip = 0;
var people = 0;
const inputs = [
document.querySelector(".bill-input"),
document.querySelector(".custom"),
document.querySelector(".people-number"),
];
inputs.forEach(function(item,index){
item.addEventListener("keyup", (e) => {
const val = Number(e.target.value);
switch(index){
case 0 : bill = val; break;
case 1 : tip = val; break;
case 2 : people = val; break;
}
console.log(bill,tip,people)
});
});
<input value="3" type="number" class="bill-input">
<input value="10" type="number" class="custom">
<input value="100" type="number" class="people-number">
I have a JSON query and I am using console.log to present it:
var json_data = {"headers":["Month","Value","Number"],"rows":[["2018-10-01 00:00:00.0","one",209],["2018-09-01 00:00:00.0","one",274],["2018-09-01 00:00:00.0","five",183],["2018-10-01 00:00:00.0","five",164],["2018-09-01 00:00:00.0","four",214],["2018-10-01 00:00:00.0","four",192],["2018-09-01 00:00:00.0","three",128],["2018-10-01 00:00:00.0","three",125],["2018-09-01 00:00:00.0","two",199],["2018-10-01 00:00:00.0","two",169],["2018-09-01 00:00:00.0","seven",10541],["2018-10-01 00:00:00.0","seven",6139],["2018-10-01 00:00:00.0","six",169],["2018-09-01 00:00:00.0","six",233]]};
document.getElementById("original").innerHTML = json_data.rows;
<div style="background:yellow; "id="original"></div>
<div style="background:red;" id="output"></div>
And for the value "one" I have two numbers (209 and 274).
I am then using a function to groupby which works fine (output). My problem is that when I use the console.log for the initial json_data query, the first two rows are different. It seems that my function replaced the first two rows with the rows of the output (red). The function is given here:
function initialGroupBy(rows) {
const
rowMap = new Map(),
result = [],
dataTemp = [];
// Iterate over the rows.
rows.forEach(row => {
const
// Create a key, it is the first elements joined together.
key = row.slice(0,1).join();
// Check if the Map has the generated key...
if (rowMap.has(key)) {
// The map has the key, we need to add up the values
const
// Get the value for the current key.
storedRow = rowMap.get(key);
// Add the value of the current row to the row in the map.
storedRow[2] += row[2];
} else {
// The key doens't exist yet, add the row to the map.
rowMap.set(key, row);
}
});
// Iterate over all the entries in the map and push each value with the
// summed up value into the array.
rowMap.forEach(value => {
result.push(value);
});
for (i = 0; i < result.length; i++)
{
var object2 = {"date": result[i][0].slice(0,7), "num": result[i][2]};
dataTemp.push(object2);
}
return dataTemp;
}
A full snippet can be found here (Compare the first two rows of the yellow box from the two snippets):
var json_data = {"headers":["Month","Value","Number"],"rows":[["2018-10-01 00:00:00.0","one",209],["2018-09-01 00:00:00.0","one",274],["2018-09-01 00:00:00.0","five",183],["2018-10-01 00:00:00.0","five",164],["2018-09-01 00:00:00.0","four",214],["2018-10-01 00:00:00.0","four",192],["2018-09-01 00:00:00.0","three",128],["2018-10-01 00:00:00.0","three",125],["2018-09-01 00:00:00.0","two",199],["2018-10-01 00:00:00.0","two",169],["2018-09-01 00:00:00.0","seven",10541],["2018-10-01 00:00:00.0","seven",6139],["2018-10-01 00:00:00.0","six",169],["2018-09-01 00:00:00.0","six",233]]};
function initialGroupBy(rows) {
const
rowMap = new Map(),
result = [],
dataTemp = [];
// Iterate over the rows.
rows.forEach(row => {
const
// Create a key, it is the first elements joined together.
key = row.slice(0,1).join();
// Check if the Map has the generated key...
if (rowMap.has(key)) {
// The map has the key, we need to add up the values
const
// Get the value for the current key.
storedRow = rowMap.get(key);
// Add the value of the current row to the row in the map.
storedRow[2] += row[2];
} else {
// The key doens't exist yet, add the row to the map.
rowMap.set(key, row);
}
});
// Iterate over all the entries in the map and push each value with the
// summed up value into the array.
rowMap.forEach(value => {
result.push(value);
});
for (i = 0; i < result.length; i++)
{
var object2 = {"date": result[i][0].slice(0,7), "num": result[i][2]};
dataTemp.push(object2);
}
return dataTemp;
}
const damn = initialGroupBy(json_data.rows);
document.getElementById("original").innerHTML = json_data.rows;
document.getElementById("output").innerHTML =JSON.stringify(damn);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="background:yellow; "id="original"></div>
<br><br>
<div style="background:red;" id="output"></div>
I have tried changing the var to const in many cases. Am I missing a fundamental JavaScript case here?
As your value is an Array object, when you save it in your temporary Map, you're actually using a reference to your original data structure row.
So in the first forEach loop, where you sum the values you're actually changing the original array entry.
The solution is pretty simple, just clone the array:
rowMap.set(key, row.slice());
Another possibility is to use a different array to save the totals.
Here is your code with the fix.
var json_data = {"headers":["Month","Value","Number"],"rows":[["2018-10-01 00:00:00.0","one",209],["2018-09-01 00:00:00.0","one",274],["2018-09-01 00:00:00.0","five",183],["2018-10-01 00:00:00.0","five",164],["2018-09-01 00:00:00.0","four",214],["2018-10-01 00:00:00.0","four",192],["2018-09-01 00:00:00.0","three",128],["2018-10-01 00:00:00.0","three",125],["2018-09-01 00:00:00.0","two",199],["2018-10-01 00:00:00.0","two",169],["2018-09-01 00:00:00.0","seven",10541],["2018-10-01 00:00:00.0","seven",6139],["2018-10-01 00:00:00.0","six",169],["2018-09-01 00:00:00.0","six",233]]};
function initialGroupBy(rows) {
const
rowMap = new Map(),
result = [],
dataTemp = [];
// Iterate over the rows.
rows.forEach(row => {
const
// Create a key, it is the first elements joined together.
key = row.slice(0,1).join();
// Check if the Map has the generated key...
if (rowMap.has(key)) {
// The map has the key, we need to add up the values
const
// Get the value for the current key.
storedRow = rowMap.get(key);
// Add the value of the current row to the row in the map.
storedRow[2] += row[2];
} else {
// The key doens't exist yet, add the row to the map.
rowMap.set(key, row.slice());
}
});
// Iterate over all the entries in the map and push each value with the
// summed up value into the array.
rowMap.forEach(value => {
result.push(value);
});
for (i = 0; i < result.length; i++)
{
var object2 = {"date": result[i][0].slice(0,7), "num": result[i][2]};
dataTemp.push(object2);
}
return dataTemp;
}
const damn = initialGroupBy(json_data.rows);
document.getElementById("original").innerHTML = json_data.rows;
document.getElementById("output").innerHTML =JSON.stringify(damn);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="background:yellow; "id="original"></div>
<div style="background:red;" id="output"></div>
A) The const declaration creates a read-only reference to a value. It does not mean the value it holds is immutable -> Link
B) Your problem is that you are actually editing the original object in the initialGroupBy function. Maybe this answer will be helpful.
A different logic applies here and the result is handy:
var json_data = {"headers":["Month","Value","Number"],"rows":[["2018-10-01 00:00:00.0","one",209],["2018-09-01 00:00:00.0","one",274],["2018-09-01 00:00:00.0","five",183],["2018-10-01 00:00:00.0","five",164],["2018-09-01 00:00:00.0","four",214],["2018-10-01 00:00:00.0","four",192],["2018-09-01 00:00:00.0","three",128],["2018-10-01 00:00:00.0","three",125],["2018-09-01 00:00:00.0","two",199],["2018-10-01 00:00:00.0","two",169],["2018-09-01 00:00:00.0","seven",10541],["2018-10-01 00:00:00.0","seven",6139],["2018-10-01 00:00:00.0","six",169],["2018-09-01 00:00:00.0","six",233]]};
function groupBy(accumulator, item) {
const [date,extra,value] = item;
const key = date.slice(0,7);
if(!accumulator[key]){
accumulator[key] = 0
}
accumulator[key] += value;
return accumulator;
}
var damn = json_data.rows.reduce(groupBy,{});
damn = Object.keys(damn).map(function(key){
return {date: key, Value: "Total", num: damn[key]};
})
document.getElementById("original").innerHTML = json_data.rows;
document.getElementById("output").innerHTML =JSON.stringify(damn);
<div style="background:yellow; "id="original"></div>
<div style="background:red;" id="output"></div>
I have the following HTML:
<input class="type" id="carbNumber">
<input class="type" id="otherId">
I want to store the input values in an object.
let data = {
inputData: []
}
document.querySelector('input').addEventListener('blur', updateItem)
function updateItem(){
data.inputData.push(this.value)
}
As I have multiple input elements I want to store their values in an array. If I input another value in the same input element - in which I had already input some other value - how can I figure out where I have stored the previous value the first time and therefore replace it with the new value?
You can use the index number from the input elements as they are returned by querySelectorAll:
const data = {
inputData: []
}
// Use the callback argument of `Array.from` and the `i` index:
Array.from(document.querySelectorAll('input'), (inp, i) => {
inp.addEventListener('input', updateItem.bind(inp, i)); // pass `i`
});
function updateItem(i){ // capture `i`
data.inputData[i] = this.value;
console.log('inputData:' + data.inputData);
}
<input id="a">
<input id="b">
<input id="c">
NB: I used the input event to display the results immediately as you type.
Simply do not use an array, use an object.
The object will map the ids of your input elements to their values
"use strict";
(function() {
var values = {};
function updateAllInputs() {
var inputs = document.querySelectorAll('input');
Array.prototype.forEach.call(inputs, function (input) {
// ensures exactly one value per input (if all inputs have ids, adjust as needed)
values[input.id] = input.value;
});
}
// if you still need an array for some reason, you can create one from the object.
function getSnapshotOfInputValuesAsArray() {
return Object.keys(values).map(function(key) {
return values[key];
});
}
}());
I have a form
<form>
<input type="text" name="Name" />
<input type="checkbox" name="Feature.Translate" />
<input type="checkbox" name="Feature.Share" />
<input type="submit" value="Convert into an object" />
</form>
I want to convert it in an object
{
Name: "John Connor's Terminator",
Feature:
{
Translate: true // if checked
// Share wasn't checked
}
}
How can I map the form to an object that has this tree structure?
Add this method to help you build the tree
// add keys to an object as a tree
// ["a", "b", "c"] will generate
// a { b: { c: def } }
// def is the value of the leaf node
var AddToTree = function(obj, keys, def)
{
for (var i = 0, length = keys.length; i < length; ++i)
obj = obj[keys[i]] = i == length - 1 ? def : obj[keys[i]] || {};
};
Create a function for a jQuery selector that will convert the form in an object
$.fn.serializeObject = function()
{
var o = {}; // final object
var a = this.serializeArray(); // retrieves an array of all form values as
// objects { name: "", value: "" }
$.each(a, function() {
var ns = this.name.split("."); // split name to get namespace
AddToTree(o, ns, this.value); // creates a tree structure
// with values in the namespace
});
return o;
};
With these two functions define you can set an event on the submit button:
$(":submit").click(function(e){
// contains the object from the form
// respecting element namespaces
var obj = $("form").serializeObject();
});
Something like the following should work:
function serializeData() {
//this is where we'll store our serialized data
var serializedData = {};
//iterate over input, select, and textarea elements
jQuery("input, select, textarea").each(function(index) {
var $element = jQuery(this);
var name = $element.attr("name");
//we only want to serialize the element if it has a 'name' attribute
if(typeof name != "undefined") {
//split on the . to get an array
var parts = name.split(/\./);
//start building the serialized data
var currentPart = serializedData;
for(var i = 0; i < parts.length; i++) {
//if this particular element doesn't already exist in our hash, create it
//and initialize it to an empty hash
if(typeof serializedData[parts[i]] == "undefined") {
currentPart[parts[i]] = {};
}
//if we're currently looking at the very last element in the array then
//it means that we need to set its value to the value of the corresponding
//input element. Otherwise, it means that there are still keys within the
//array and so we set `currentPart` to the new hash that we just created
if(i == parts.length - 1) {
//if the element is a checkbox or a radio, we need to see if it's checked
//instead of looking at its value
if($element.attr("type").toLowerCase() == "checkbox" || $element.attr("type").toLowerCase() == "radio") {
currentPart[parts[i]] = $element.is(":checked");
}
else {
currentPart[parts[i]] = $element.val();
}
}
else {
currentPart = currentPart[parts[i]];
}
}
}
});
console.log(serializedData);
}
Check out the fiddle.
All you need to do now is to bind serializeData to the submit event on the form.