I'm trying to create a function which takes an array and returns the average of it. The Id
argument is the html id that I've passed into the function.
I don't know why but I am getting an error:
script.js:12 Uncaught TypeError: Cannot read properties of undefined (reading 'forEach') at avgFinder (script.js:12:12) at HTMLButtonElement.onclick (one.html:21:49)
let array = [];
const addToarray = (id) => {
return array.push(id);
};
const avgFinder = () => {
let total = 0;
let count = 0;
array.forEach(item => {
total = item;
count ;
});
console.log(total / count);
return total / count;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="script.js"></script>
</head>
<body>
<div id="1" onclick="addToarray(1)">1</div>
<div id="2" onclick="addToarray(2)">2</div>
<div id="3" onclick="addToarray(3)">3</div>
<div id="4" onclick="addToarray(4)">4</div>
<div id="5" onclick="addToarray(5)">5</div>
<div id="6" onclick="addToarray(6)">6</div>
<div id="7" onclick="addToarray(7)">7</div>
<div id="8" onclick="addToarray(8)">8</div>
<div id="9" onclick="addToarray(9)">9</div>
<div id="10" onclick="addToarray(10)">10</div>
<button onclick="avgFinder()">Click me</button>
</body>
</html>
CodePudding user response:
Your avgFinder = (arr) => {
expects an arr
Array, instead you're passing... nothing.
Also, let total = 0;
cannot be a hardcoded 0
. It should be your array.length
instead.
Here's a fix with best practices:
- Avoid using inline HTML
on*
JS handlers. Use the proper addEventListener() isnstead. JS should be in one place only, and that's the respective tag or file. - Use data-* attribute to store arbitrary data that can be used in JavaScript.
- Use Array.prototype.reduce to reduce an Array to Number.
- Avoid polluting the GlobalThis scope by using
window
or undeclared variables. Try always to useconst
, orlet
when a value is expected to be changed at a later time.
// DOM utility functions:
const el = (sel, par) => (par || document).querySelector(sel);
const els = (sel, par) => (par || document).querySelectorAll(sel);
// Task: Find average:
const elsDataId = els("[data-id]");
const elAvgFinder = el("#avgFinder");
const elItems = el("#items");
const elResult = el("#result");
const array = [];
const addToArray = (n) => array.push(n);
const avgFinder = () => {
const count = array.reduce((acc, n) => acc n, 0);
const avg = count / array.length;
elItems.value = array;
elResult.value = avg;
return avg
}
elsDataId.forEach((elDataId) => {
elDataId.addEventListener("click", () => addToArray( elDataId.dataset.id));
});
elAvgFinder.addEventListener("click", avgFinder);
<button data-id="1" type="button">1</button>
<button data-id="2" type="button">2</button>
<button data-id="3" type="button">3</button>
<button data-id="4" type="button">4</button>
<button data-id="5" type="button">5</button>
<button data-id="6" type="button">6</button>
<button data-id="7" type="button">7</button>
<button data-id="8" type="button">8</button>
<button data-id="9" type="button">9</button>
<button data-id="10" type="button">10</button>
<button id="avgFinder" type="button">Calculate Avg</button><br>
Items: <input id="items" type="text" readonly><br>
Average: <input id="result" type="text" readonly>
CodePudding user response:
Previous edits of this answer gave a solution that was less than idea. The following addresses those:
- No pollution of globalThis
- Does not mix functionality with markup
- Does not mix data with functionality
// wrapping function to avoid poluting globalThis
(function(){
// data-store for values
let arr = [];
// Loop over each button that has a data-id
document.querySelectorAll('button[data-id]').forEach(ele => {
// Add event listener: when clicked add the button's value to the array
ele.addEventListener('click', () => arr.push(Number(ele.dataset.id)));
});
// Get a reference to the result element
const averageEle = document.getElementById('result');
// Add event listener for when the average button is clicked
document
.getElementById('avgFinder')
.addEventListener('click', () => {
// no values to average
if (arr.length === 1) {
averageEle.value = 0;
// nothing to average
} else if (arr.length === 1) {
averageEle.value = arr[0];
} else {
const sum = arr.reduce((prev, cur) => prev cur);
const average = sum / arr.length;
averageEle.value = average;
}
});
})();
<button data-id="1" type="button">1</button>
<button data-id="2" type="button">2</button>
<button data-id="3" type="button">3</button>
<button data-id="4" type="button">4</button>
<button data-id="5" type="button">5</button>
<button data-id="6" type="button">6</button>
<button data-id="7" type="button">7</button>
<button data-id="8" type="button">8</button>
<button data-id="9" type="button">9</button>
<button data-id="10" type="button">10</button>
<button id="avgFinder" type="button">Calculate Avg</button><br>
Average: <input id="result" type="text" value="0" readonly>