Why is array seemingly empty after the loop? [duplicate] - javascript

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
function par(idF, idM) {
this.IDOvna = idM;
this.IDOvce = idF;
}
function breeding() {
let idOvce = [];
let brOvce = [];
let mesecOvce = [];
let godinaOvce = [];
let istorija1 = [];
let istorija2 = [];
let idOvna = [];
let brOvna = [];
let mesecOvna = [];
let godinaOvna = [];
let y = 0;
let o = 0;
let parovi = [];
let c = 0;
fetch("http://localhost/ovce/ovce.json")
.then(function(resp) {
return resp.json();
})
.then(function(data) {
console.log(data);
for (let i = 0; i < data.ovce.length; i++) {
idOvce[i] = data.ovce[i].id;
brOvce[i] = data.ovce[i].broj;
mesecOvce[i] = data.ovce[i].mesec;
godinaOvce[i] = data.ovce[i].godina;
istorija1[i] = data.ovce[i].istorija1;
istorija2[i] = data.ovce[i].istorija2;
}
});
fetch("http://localhost/ovce/ovnovi.json")
.then(function(resp1) {
return resp1.json();
})
.then(function(data1) {
console.log(data1);
for (let g = 0; g < data1.ovnovi.length; g++) {
idOvna[g] = data1.ovnovi[g].id;
brOvna[g] = data1.ovnovi[g].broj;
mesecOvna[g] = data1.ovnovi[g].mesec;
godinaOvna[g] = data1.ovnovi[g].godina;
}
});
while (o < idOvna.length) {
y = 0;
while (y < idOvce.length) {
if (istorija1[y] != 0) {
if ((istorija2[y] != idOvna[o]) && (istorija2[istorija1[y]] != idOvna[o])) {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
} else {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
y++;
}
o++;
}
console.log(parovi);
return parovi;
}
<html>
<head>
<title>Sheepify</title>
<script src="main.js"></script>
</head>
<body>
<button onclick="breeding()"> Breeding </button>
</body>
</html>
In javascript, i have a loop running and afterwards the pairs array which should be populated is empty.
function pair(idF, idM) {
this.IDOvna = idM;
this.IDOvce = idF;
}
function problem() {
let y = 0;
let o = 0;
let pairs = [];
let c = 0;
//id, id1, history1, history2 are arrays which are populated from the json files using fetch.
while (o < id.length) {
y = 0;
while (y < id1.length) {
if (history1[y] != 0) {
if ((history2[y] != id[o]) && (history2[history1[y]] != id[o])) {
pairs[c] = new pair(id1[y], id[o]);
c++;
}
} else {
pairs[c] = new pair(id1[y], id[o]);
c++;
}
y++;
}
o++;
}
console.log(pairs);
console.log(pairs.length);
}
When i run a debugger the array is populated and everything is fine, but when i execute the function on button click or through a console, it just returns an empty array. What could be causing this problem?
EDIT: I accidentally pasted the pair function inside the problem function, which isn't the case. I have moved it out now. And changed leght to length as suggested.
EDIT2: Here is the full code, sorry about the variable names, they are in Serbian.

The loop that processes the arrays is outside the fetch callback functions, so it doesn't wait for the arrays to be populated. See Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference.
You can make your function async and use await to wait for them.
Note also that the code that calls this will need to use await or .then() to get the returned parovi array. See How do I return the response from an asynchronous call?
async function breeding() {
let idOvce = [];
let brOvce = [];
let mesecOvce = [];
let godinaOvce = [];
let istorija1 = [];
let istorija2 = [];
let idOvna = [];
let brOvna = [];
let mesecOvna = [];
let godinaOvna = [];
let y = 0;
let o = 0;
let parovi = [];
let c = 0;
await fetch("http://localhost/ovce/ovce.json")
.then(function(resp) {
return resp.json();
})
.then(function(data) {
console.log(data);
for (let i = 0; i < data.ovce.length; i++) {
idOvce[i] = data.ovce[i].id;
brOvce[i] = data.ovce[i].broj;
mesecOvce[i] = data.ovce[i].mesec;
godinaOvce[i] = data.ovce[i].godina;
istorija1[i] = data.ovce[i].istorija1;
istorija2[i] = data.ovce[i].istorija2;
}
});
await fetch("http://localhost/ovce/ovnovi.json")
.then(function(resp1) {
return resp1.json();
})
.then(function(data1) {
console.log(data1);
for (let g = 0; g < data1.ovnovi.length; g++) {
idOvna[g] = data1.ovnovi[g].id;
brOvna[g] = data1.ovnovi[g].broj;
mesecOvna[g] = data1.ovnovi[g].mesec;
godinaOvna[g] = data1.ovnovi[g].godina;
}
});
while (o < idOvna.length) {
y = 0;
while (y < idOvce.length) {
if (istorija1[y] != 0) {
if ((istorija2[y] != idOvna[o]) && (istorija2[istorija1[y]] != idOvna[o])) {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
} else {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
y++;
}
o++;
}
console.log(parovi);
return parovi;
}
function par(idF, idM) {
this.IDOvna = idM;
this.IDOvce = idF;
}
<html>
<head>
<title>Sheepify</title>
<script src="main.js"></script>
</head>
<body>
<button onclick="breeding()"> Breeding </button>
</body>
</html>

how are you using this problem function within the fetch?
If you are using int outside the fetch its totaly normal, your arrays will be empty.
Edit:
As you have post the fetch method, you need to put all your while loop un the last then, when you are populating the data, because fetch is async =/
Since you have two fetch, and both are populating the array's you need to do the second in the last then of the first fetch. And finaly you can do your while loop in last then of your second fetch.
It may be more clearner using async await method.
function par(idF, idM) {
this.IDOvna = idM;
this.IDOvce = idF;
}
function breeding() {
let idOvce = [];
let brOvce = [];
let mesecOvce = [];
let godinaOvce = [];
let istorija1 = [];
let istorija2 = [];
let idOvna = [];
let brOvna = [];
let mesecOvna = [];
let godinaOvna = [];
let y = 0;
let o = 0;
let parovi = [];
let c = 0;
fetch("http://localhost/ovce/ovce.json")
.then(function(resp) {
return resp.json();
})
.then(function(data) {
console.log(data);
for (let i = 0; i < data.ovce.length; i++) {
idOvce[i] = data.ovce[i].id;
brOvce[i] = data.ovce[i].broj;
mesecOvce[i] = data.ovce[i].mesec;
godinaOvce[i] = data.ovce[i].godina;
istorija1[i] = data.ovce[i].istorija1;
istorija2[i] = data.ovce[i].istorija2;
}
fetch("http://localhost/ovce/ovnovi.json")
.then(function(resp1) {
return resp1.json();
})
.then(function(data1) {
console.log(data1);
for (let g = 0; g < data1.ovnovi.length; g++) {
idOvna[g] = data1.ovnovi[g].id;
brOvna[g] = data1.ovnovi[g].broj;
mesecOvna[g] = data1.ovnovi[g].mesec;
godinaOvna[g] = data1.ovnovi[g].godina;
while (o < idOvna.length) {
y = 0;
while (y < idOvce.length) {
if (istorija1[y] != 0) {
if ((istorija2[y] != idOvna[o]) && (istorija2[istorija1[y]] != idOvna[o])) {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
} else {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
y++;
}
o++;
}
console.log(parovi);
return parovi;
}
});
});
}

Related

JSON.stringify not working with nested array of objects

I know this question has been asked but none of the solutions are working for me and I can't figure out what's wrong. I have an object with a nested array of objects I am stringifying but I get a blank array of the nested array when I use JSON.stringify on it.
This is a simplified version of the way I'm constructing the object. The main difference is that there is a for loop iterating through all the rows, here I am just manually creating 2 rows
// JAVASCRIPT
let obj = {};
obj['type'] = 'Setting';
obj['id'] = 1;
obj['import'] = parseCSV();
function parseCSV() {
let jsonData = [];
let row1 = {};
let row2 = {};
row1['date'] = '2022-01-01';
row1['amount'] = '30';
row2['date'] = '2022-01-02';
row2['amount'] = '50';
jsonData.push(row1);
jsonData.push(row2);
return jsonData;
}
console.log('RAW DATA', obj);
console.log('STRINGIFIED', JSON.stringify(obj));
The above outputs the correct stringified JSON
But the full version of my code gives me a blank array for import.
Both objects look identical to me. The culprit is somewhere in my parseCSV function, because when I use the simplified version above I get the correct stringified data, but I can't pinpoint where I'm wrong. Below is my full function.
function parseCSV(file) {
let filename = file.name;
let extension = filename.substring(filename.lastIndexOf('.')).toUpperCase();
if(extension == '.CSV') {
try {
let reader = new FileReader();
let jsonData = [];
let headers = [];
reader.readAsBinaryString(file);
reader.onload = function(e) {
let rows = e.target.result.split('\n');
for(let i = 0; i < rows.length; i++) {
let cells = rows[i].split(',');
let rowData = {};
for(let j = 0; j < cells.length; j++) {
if(i == 0) headers.push(cells[j].trim());
else {
if(headers[j]) rowData[headers[j]] = cells[j].trim();
}
}
if(i != 0 && rowData['date'] != '') jsonData.push(rowData);
}
}
return jsonData;
} catch(err) {
console.error('!! ERROR READING CSV FILE', err);
}
} else alert('PLEASE UPLOAD A VALID CSV FILE');*/
}
Thanks for the help!
EDIT
When I add await before parseCSV as #BJRINT's answer suggests I get a syntax error await is only valid in async function
async function submitForm(event) {
event.preventDefault();
let newItem = await gatherFormData(event.target);
return fetch('server.php', {
method: "POST",
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(newItem)
})
.then(checkError)
.then(data => parseData(data))
.catch(err => console.error('>> ERROR READING JSON DATA', err));
}
function gatherFormData(target) {
const inputs = target.querySelectorAll('input');
let obj = {};
inputs.forEach(function(input) {
if(intKeys.indexOf(input.name) >= 0) obj[input.name] = parseInt(input.value);
else if(curKeys.indexOf(input.name) >= 0) obj[input.name] = parseInt(parseFloat(input.value) * 100);
else if(chkKeys.indexOf(input.name) >= 0) input.checked ? obj[input.name] = 1 : obj[input.name] = 0;
else if(fileKeys.indexOf(input.name) >= 0 && input.files.length > 0) obj[input.name] = parseCSV(input.files[0]);
else obj[input.name] = input.value;
});
return obj;
}
The problem does not come from the stringify function. Since you are filling your array asynchronously (when the reader callback is executed) but returning your data first, it is empty.
You could wrap your function with a Promise that resolves when the reader callback function is finally executed, like so:
function parseCSV(file) {
return new Promise((resolve, reject) => {
let filename = file.name;
let extension = filename.substring(filename.lastIndexOf('.')).toUpperCase();
if(extension !== '.CSV')
return reject('PLEASE UPLOAD A VALID CSV FILE')
try {
let reader = new FileReader();
let jsonData = [];
let headers = [];
reader.readAsBinaryString(file);
reader.onload = function(e) {
let rows = e.target.result.split('\n');
for(let i = 0; i < rows.length; i++) {
let cells = rows[i].split(',');
let rowData = {};
for(let j = 0; j < cells.length; j++) {
if(i == 0) headers.push(cells[j].trim());
else {
if(headers[j]) rowData[headers[j]] = cells[j].trim();
}
}
if(i != 0 && rowData['date'] != '') jsonData.push(rowData);
}
return resolve(jsonData);
}
} catch(err) {
return reject('!! ERROR READING CSV FILE', err);
}
})
}
// calling the function
const data = await parseCSV(file)
The solution that worked for my specific case was to use a combination of BJRINT's answer and a timer to keep checking if the data had finished loading which I found here.
async function parseCSV(file) {
return await new Promise((resolve, reject) => {
let extension = file.name.substring(file.name.lastIndexOf('.')).toUpperCase();
if(extension !== '.CSV') reject('PLEASE UPLOAD A VALID CSV FILE');
try {
let reader = new FileReader();
reader.readAsText(file);
reader.onload = function(e) {
let jsonData = [];
let headers = [];
let rows = e.target.result.split(/\r\n|\r|\n/);
for(let i = 0; i < rows.length; i++) {
let cells = rows[i].split(',');
let rowData = {};
for(let j = 0; j < cells.length; j++) {
if(i == 0) headers.push(cells[j].trim());
else {
if(headers[j]) rowData[headers[j]] = cells[j].trim();
}
}
if(i != 0 && rowData['date'] != '') jsonData.push(rowData);
}
resolve(jsonData);
}
} catch(err) {
reject(err);
}
});
}
function submitForm(event) {
event.preventDefault();
showForm(false);
loading.classList.remove('hidden');
let ready = true;
const inputs = event.target.querySelectorAll('input');
let newItem = {};
let check = function() {
if(ready === true) {
console.log(newItem);
console.log(JSON.stringify(newItem));
return fetch('server.php', {
method: "POST",
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(newItem)
})
.then(checkError)
.then(data => parseData(data))
.catch(err => console.error('>> ERROR READING JSON DATA', err));
}
setTimeout(check, 1000);
}
inputs.forEach(function(input) {
if(intKeys.indexOf(input.name) >= 0) newItem[input.name] = parseInt(input.value);
else if(curKeys.indexOf(input.name) >= 0) newItem[input.name] = parseInt(parseFloat(input.value) * 100);
else if(chkKeys.indexOf(input.name) >= 0) input.checked ? newItem[input.name] = 1 : newItem[input.name] = 0;
else if(fileKeys.indexOf(input.name) >= 0 && input.files.length > 0) {
ready = false;
parseCSV(input.files[0]).then(data => {
ready = true;
newItem[input.name] = data;
});
}
else newItem[input.name] = input.value;
});
check();
}

Array.find to update a value in a nested array

I created a basket in JavaScript, it works perfectly except for one thing:
When we add products to the basket, we select a quantity and everything goes well, but if we click again to add the same product, instead of modifying the quantity of the product, the same product is added to the localStorage.
To avoid this problem, I have to search for the product in the localStorage with the Array.find() method, if the product exists, I modify the quantity, otherwise I add it. But I don't know how to do it the right way yet
let basket = JSON.parse(localStorage.getItem('monPanier')) || [];
let wishlist = JSON.parse(localStorage.getItem('myWishList')) || [];
let totalBasket = 0;
let nbrArticles;
let nbrWishlist;
function addBasket()
{
let quantity_cart = $(".input_quantity"+$(this).data("id")).val();
let name_article_cart = $(this).data("name");
let idArticle = parseInt(this.id.replace("add_basket_btn_",""));
let price_cart = $(this).data("price");
let total_price_cart = (quantity_cart * price_cart);
let newBasket = [idArticle,quantity_cart,name_article_cart,price_cart,total_price_cart];
basket.push(newBasket);
saveStorageBasket();
loadStorageBasket();
showBasket();
calculeNbrArticle();
console.log(newBasket);
}
function addWishlist()
{
let name_article_wishlist = $(this).data("name");
let idArticle = parseInt(this.id.replace("add_wishlist_btn_",""));
let price_article_wishlist = $(this).data("price");
let newWishlist = [idArticle,name_article_wishlist,price_article_wishlist];
wishlist.push(newWishlist);
saveStorageWishlist();
loadStorageWishlist();
showWishList();
calculeNbrWishlist();
}
function showBasket() {
//vider le panier
$("#basket").empty();
totalBasket = 0;
for(let i=0; i < basket.length; i++)
{
totalBasket = totalBasket + basket[i][4];
$("#basket").append("<tr><td>"+basket[i][1]+"</td>"
+"<td>"+basket[i][2]+"</td>"
+"<td>"+basket[i][3]+"€</td>"
+"<td>"+basket[i][4]+"€</td>"
+"<td><button data-id='"+i+"' class='btn_cancel_basket'>supprimer</button></td>"
+"</tr>");
}
// installer un event
$("#basket button.btn_cancel_basket").on("click",suppArticleBasket);
$("#totalBasket").text(totalBasket+" €");
}
function suppArticleBasket()
{
let index = $(this).data('id');
console.log(index);
basket.splice(index,1);
saveStorageBasket();
loadStorageBasket();
showBasket();
}
function showWishList()
{
for(let i=0; i < wishlist.length; i++)
{
$("#Wishlist").append("<tr><td>"+wishlist[i][1]+"</td>"
+"<td>"+wishlist[i][2]+"</td>"
+"</tr>");
}
}
function emptyBasket()
{
if(basket == null || basket == 0)
{
console.log("panier = true");
return true;
}else
{
console.log("panier = false");
return false;
}
}
function validateBasket()
{
if(emptyBasket() == false)
{
loadStorageBasket();
basket = JSON.stringify(basket); //js --> json
console.log($.get("index.php","action=appelAjax2&Basket="+basket+"&total="+totalBasket,resetBasket));
}
}
function resetBasket(reponse){
$("#confirmationcmd").html("<p>Commande validée.</p>");
$("#basket").empty();
basket.length = 0;
localStorage.clear();
loadStorageBasket();
showBasket();
totalBasket = 0;
calculeNbrArticle();
}
function resetBasket2(reponse){
$("#confirmationcmd").html("<p>Panier annulé.</p>");
$("#basket").empty();
basket.length = 0;
localStorage.clear();
loadStorageBasket();
showBasket();
totalBasket = 0;
calculeNbrArticle();
}
function resetWishlist(reponse){
$("#confirmationWishlist").html("<p>Wishlist remise à zéro.</p>");
$("#wishlist").empty();
wishlist.length = 0;
localStorage.clear();
loadStorageWishlist();
showWishList();
totalWishlist = 0;
calculeNbrWishlist();
}
function saveStorageWishlist(){
localStorage.setItem("myWishList", JSON.stringify(wishlist));
}
function loadStorageWishlist(){
wishlist = localStorage.getItem("myWishList");
if(wishlist == null)
{
wishlist =[];
}
else
{
wishlist = JSON.parse(wishlist);
}
}
function calculeNbrWishlist()
{
loadStorageWishlist();
nbrWishlist = wishlist.length;
$("#nbrWishlist").text("("+nbrWishlist+")");
}
function saveStorageBasket(){
localStorage.setItem("monPanier", JSON.stringify(basket));
}
function loadStorageBasket(){
basket = localStorage.getItem("monPanier");
if(basket == null)
{
basket =[];
}
else
{
basket = JSON.parse(basket);
}
}
function calculeNbrArticle()
{
loadStorageBasket();
nbrArticles = basket.length;
$("#nbrArticles").text("("+nbrArticles+")");
}
$(function()
{
calculeNbrArticle();
calculeNbrWishlist();
loadStorageBasket();
loadStorageWishlist();
showBasket();
showWishList();
$(".add_basket_btn").on('click',addBasket);
$(".btn_wishlist").on('click',addWishlist);
$("#validateButton").on('click',validateBasket);
$("#resetButton").on('click',resetBasket2);
$("#resetButtonWishlist").on('click',resetWishlist);
});
The basic idea:
push the new basket array if empty
check if the ID is already in the basket array
if so, update the quantity
if not, add the new basket
I have read the following learning resources about:
for loops (w3schools)
find (MDN)
filter (MDN)
According to what I have read in this post Array.find does not work because it finds only the first element. I used the suggested filter option.
The essential part with changes:
function updateQuantity(basket, newBasket, idArticle)
{
// filter: search for articleId in array of arrays
if (basket.filter(({ 1: n }) => n === idArticle)) {
for(i = 0; i < basket.length; i++) {
basket[i][1] += 1;
}
} else {
basket.push(newBasket)
}
}
function addBasket()
{
let quantity_cart = 1
let name_article_cart = "productName"
let idArticle = 1
let price_cart = 10
let total_price_cart = (quantity_cart * price_cart);
let newBasket = [idArticle,quantity_cart,name_article_cart,price_cart,total_price_cart];
if (basket.length === 0) {
basket.push(newBasket)
}
else
{
updateQuantity(basket, newBasket, idArticle)
}
console.log(basket)
}
My complete test code:
let basket = JSON.parse(localStorage.getItem('monPanier')) || [];
let totalBasket = 0;
let nbrArticles;
function updateQuantity(basket, newBasket, idArticle)
{
// filter: search for articleId in array of arrays
if (basket.filter(({ 1: n }) => n === idArticle)) {
for(i = 0; i < basket.length; i++) {
basket[i][1] += 1;
}
} else {
basket.push(newBasket)
}
}
function addBasket()
{
let quantity_cart = 1
let name_article_cart = "productName"
let idArticle = 1
let price_cart = 10
let total_price_cart = (quantity_cart * price_cart);
let newBasket = [idArticle,quantity_cart,name_article_cart,price_cart,total_price_cart];
if (basket.length === 0) {
basket.push(newBasket)
}
else
{
updateQuantity(basket, newBasket, idArticle)
}
console.log(basket)
}
function suppArticleBasket()
{
let index = 1
console.log(index);
basket.splice(index,1);
saveStorageBasket();
loadStorageBasket();
}
function emptyBasket()
{
if(basket == null || basket == 0)
{
return true;
} else
{
return false;
}
}
function saveStorageBasket(){
localStorage.setItem("monPanier", JSON.stringify(basket));
}
function loadStorageBasket(){
basket = localStorage.getItem("monPanier");
if(basket == null)
{
basket =[];
}
else
{
basket = JSON.parse(basket);
}
}
$(function()
{
loadStorageBasket();
$("#add_basket_btn").on('click',addBasket);
});
<button id="add_basket_btn">Add</button>

Async/Await + Ajax .done: Returning undefined

Sorry if this is extremely simple, I can't get my head around it. I've seen similar questions but nothing which gets to the heart of my problem, I think. I have a simple async function:
async function isOpen() {
var open;
var data = $.ajax({
//Working code here
});
data.done(function(dat) {
var obj = JSON.parse(dat);
var msg = "";
for (var i = 0; i < obj.length; i++) {
let closingDate = Date.parse(obj[i].close);
let openingDate = Date.parse(obj[i].open);
if (closingDate > Date.now() && openingDate < Date.now()) {
open = true;
} else {
open = false;
}
}
});
return open;
}
I know that this code is all working - using console.log I have seen that open is always successfully assigned to true or false. So I call this async function from another async function:
async function testFunction(){
const open1 = await isOpen();
//More code here...
}
But open1 (in testFunction) is always undefined - even though I use await.
Any ideas what this could be?
async function isOpen() {
var open;
var data = await $.ajax({
//Working code here
});
var obj = JSON.parse(data);
var msg = "";
for (var i = 0; i < obj.length; i++) {
let closingDate = Date.parse(obj[i].close);
let openingDate = Date.parse(obj[i].open);
if (closingDate > Date.now() && openingDate < Date.now()) {
open = true;
} else {
open = false;
}
}
return open;
}
of course, the value of open will be determined by the last obj[i]
So you want
async function isOpen() {
const data = await $.ajax({
//Working code here
});
const obj = JSON.parse(data);
let msg = "";
for (var i = 0; i < obj.length; i++) {
const closingDate = Date.parse(obj[i].close);
const openingDate = Date.parse(obj[i].open);
if (closingDate > Date.now() && openingDate < Date.now()) {
return true;
}
}
return false;
}
Or even
async function isOpen() {
const data = await $.ajax({
//Working code here
});
const obj = JSON.parse(data);
let msg = "";
return obj.some(({open, close}) => {
const closingDate = Date.parse(close);
const openingDate = Date.parse(open);
const now = Date.now();
return closingDate > now && openingDate < now();
});
}
This is happening because you are using a callback for ajax request and not waiting for ajax request to complete, which is going to set open in isOpen. You can return a Promise to resolve this,
function isOpen() {
return new Promise((resolve, reject) => {
var open;
var data = $.ajax({
//Working code here
});
data.done(function(dat) {
var obj = JSON.parse(dat);
var msg = "";
for (var i = 0; i < obj.length; i++) {
let closingDate = Date.parse(obj[i].close);
let openingDate = Date.parse(obj[i].open);
if (closingDate > Date.now() && openingDate < Date.now()) {
open = true;
} else {
open = false;
}
}
resolve(open);
});
}
}
Note: For the above code, you need to handle the logic for errors in the callback, using reject otherwise it will hang forever.

neural network backprop algorithm implementation question

I've been writing a neural network from scratch to learn from.
but since i'm still learning - I want to make sure what I'm writing is actually correct.
I have an array of arrays (a matrix), of cell objects. attached to a 'brain' object which has the following method two methods:
train: function(data)
{
for (let b=0; b< data.length; b++)// for the length of the training data - I.E. we are going assume we are getting many relatively shortly indexed arrays
{
if(data[b].answers.length != data[b].questions.length)
{
console.log("bad data");
return false;
}
for(let c=0;c<data[b].questions.length;c++)
{
brain.attachInputLayer(data[b].questions[c]);
brain.updateForward();
let direction = brain.determinDirection(data[b].answers[c]); //return an array of updateObject with determined weights bias value adjustments, which each cell gets updated order should be from generation by column;
brain.cellMatrix.forEach(cellArray=> cellArray.forEach(cell=> cell.adjust(direction.find(x=> x.ID ===cell.ID))));
brain.updateForward();
brain.displayBrain();
}
}
console.log("all training data done");
alert("win?");
console.log(brain.cellMatrix);
console.log("brain");
}
and
determinDirection:function(answer)
{
// answer is the array of values of each answer cell we want as a result
let arrayOfUpDateObjectsForCell = [];
for(let e=0; e<answer.length; e++)
{
let answerCell = brain.cellMatrix[cellMatrix.length-1][e];
let returnBucket = [];
arrayOfUpDateObjectsForCell.push(answerCell.whatIwant(answer[e], returnBucket));
}
let list = Flat(arrayOfUpDateObjectsForCell);
let realList = Clean(list);
return realList;
}
so each cell of the last generation (the answer output) calls the whatIwant method at brain.train(), this function propagates backwards through the network... but my question really is this:::
:::
does it look like I am calculating the error / direction to move each value correctly?
is averaging the changes between the duplicated updateObjects correct?
(the desiredObjectchange for cell.gen=3,order=0 gets created from each of the next layer cells calling whatIwant. the changes cell.gen=4,order=0 wants cell.gen=3,order=0 to have is averaged with the changes cell.gen=4,order=1 wants for cell.gen=3,order=0).
is averaging the correct operation here?
:::
whatIwant:function(answerValue, returnArray)
{
let desiredWeights = this.weights;
let desiredBias = this.bias;
let desiredActivations = this.activations;
let error = (1/2)*Math.pow(cell.value-answerValue,2);
let desiredObjectChange =
{
ID:this.ID,
weights:this.weights,
bias:this.bias,
activations:this.activations,
value:answerValue,
combine:function(yourCloneFriend)
{
if(yourCloneFriend == false)
{
return;
}
this.bias = (1/2)*(this.bias+yourCloneFriend.bias);
let cWeight = yourCloneFriend.weights[Symbol.iterator]();
let cActivations = yourCloneFriend.activations[Symbol.iterator]();
this.weights.forEach(x=> (1/2)*(x+cWeight.next().value));
this.activations.forEach(y=> (1/2)*(y+cActivations.next().value));
this.recalculateValue();
return this;
},
recalculateValue:function()
{
this.value = Sigmoid(arrayMultiply(this.weights, this.activations)+this.bias);
}
}
for(let k = 0; k< this.weights.length; k++)
{
let lastValue = Sigmoid(arrayMultiply(desiredWeights, desiredActivations)+desiredBias);
let lastError = (1/2)*Math.pow(lastValue-answerValue,2);
for(let l=0;l<3;l++)
{
let currentValue = Sigmoid(arrayMultiply(desiredObjectChange.weights, desiredObjectChange.activations)+desiredObjectChange.bias);
let currentError = (1/2)*Math.pow(currentValue-answerValue,2);
let positiveRange = false;
if(desiredWeights[k] < 0){ positiveRange = true;}
let nudgedWeightArray = NudgeArray(desiredWeights, k, l, positiveRange); //returns a copy array to test, with weight adjusted.
let testWeightChange = Sigmoid(arrayMultiply(nudgedWeightArray,desiredActivations)+desiredBias);
let testWeightError = (1/nudgedWeightArray.length)*Math.pow(testWeightChange - answerValue, 2);
let testWeightResult = compareSmallnumbers('isSmaller', currentError, testWeightError);
if(testWeightResult);
{
desiredWeights = nudgedWeightArray;
currentError = testWeightError;
}
positiveRange=false;
if(desiredBias < 0){positiveRange = true;}
let nudgedBiasVal = this.nudge(desiredBias,l,positiveRange);
let testBiasChange = Sigmoid(nudgedBiasVal+desiredWeights[k]*desiredActivations[k]);
let testBiasError = (1/1)*Math.pow(testBiasChange - answerValue, 2);
let testBiastResult = ('isSmaller', currentError, testBiasError);
if(testBiastResult);
{
desiredBias = nudgedBiasVal;
currentError = testBiasError;
}
positiveRange=!!Math.random(0,1)>5;
let nudgedAcitivationArray = NudgeArray(desiredActivations,k,l,positiveRange);
let testActivationChange = Sigmoid(arrayMultiply(nudgedAcitivationArray,desiredWeights)+desiredBias);
let testActivationError = (1/nudgedAcitivationArray.length)*Math.pow(testActivationChange - answerValue, 2);
let testActivationResult = compareSmallnumbers('isSmaller', currentError, testActivationError);
if(testActivationResult);
{
desiredActivations[k] = nudgedAcitivationArray[k];
currentError = testActivationError;
}
//and the end of the loop, update the error to the new value
let errorResult = compareSmallnumbers('isSmaller',lastError, currentError);
if(errorResult)
{
lastError = currentError;
}
}
desiredObjectChange.weights[k] = desiredWeights[k];
desiredObjectChange.bias = desiredBias;
desiredObjectChange.activations[k] = desiredActivations[k];
desiredObjectChange.value = Sigmoid(arrayMultiply(desiredObjectChange.weights, desiredObjectChange.activations)+desiredObjectChange.bias);
}
let combineObject = returnArray.find(x=>x.ID === desiredObjectChange.ID);
if(!combineObject)
{
returnArray.push(desiredObjectChange);
}
//that was this object - simple stuff, now we need to call this function
if(Array.isArray(cell.lastGenerationTargetKeys) && cell.lastGenerationTargetKeys.length)
{
let nextActivation = desiredObjectChange.activations[Symbol.iterator]();
brain.cellMatrix[cell.generation-1].forEach(x=> x.whatIwant(nextActivation.next().value, returnArray));
return returnArray;
}
else
{
return;
}
},
clean,flat and NudgeArray are these::
function Clean(array)
{
let rArray = [];
array.forEach((x)=>
{
let search = rArray.find(y=>y.ID ===x.ID);
if(search === undefined)
{
rArray.push(x);
}
else
{
rArray[rArray.indexOf(search)].combine(x);
}
});
return rArray;
}
function Flat(array)
{
let holdBucket = [];
let flatten = function(array)
{
for(let i = 0; i<array.length;i++)
{
if(Array.isArray(array[i]))
{
flatten(array[i]);
}
else
{
holdBucket.push(array[i]);
}
}
}
flatten(array);
return holdBucket;
}
function NudgeArray(array ,arrayIndex, Nudgeindex, isPositive)
{//nudge index is designed to act like a variable learning rate modifier, as it tests, jumps decrease in size
let returnArray = [];
array.forEach(x=>returnArray.push(x));
let value = returnArray[arrayIndex];
if(isPositive)
{
value+=(Math.random(0,1)/(Nudgeindex+3));
value = Sigmoid(value);
}
else
{
value+=(Math.random(-1,1)/(Nudgeindex+3));
value = Sigmoid(value);
}
returnArray.splice(arrayIndex,1,value);
return returnArray;
}

Push to an array in a nested loop is repeating the last value

I am trying to push elements to an array in a nested loop, but only the last item is getting repeated in the final array, where am I going wrong, very new to javascript asynchronous concept.Below is the function in which I push the items to an array.
$scope.showBeList = function(feed) {
if (feed[srcServ.KEY_CONTENT_TEXT]) {
var content = JSON.parse(feed[srcServ.KEY_CONTENT_TEXT])
if (content) {
$scope.beList = {};
for (var key in content) {
var decorationVal;
//reading value from a lokijs collection
var decoration = dataServ[srcServ.CONST_COLLECTION_DECORATION].find({
'name': key
});
if (decoration && decoration.length) {
decorationVal = decoration[0];
if (decorationVal != null) {
var tempObj = JSON.parse(decorationVal.value);
if (tempObj) {
var header = tempObj[key][key + '_HEADER'];
if (header) {
var counter = content[key].length;
var tempItems = [];
for (var j = 0; j < content[key].length; j++) {
(function(j) {
var obj = {};
obj[srcServ.KEY_MAIN_HEADER] = tempObj[key][srcServ.KEY_DESC];
obj[srcServ.KEY_SUB_HEADER] = header[srcServ.KEY_DESC];
obj.id = j;
var itemVal = content[key][j][key + '_HEADER'];
var details = [];
var notes = [];
for (var item in itemVal) {
var val = null;
var found = false;
for (var i = 0; i < header.field.length; i++) {
if (header.field[i].name == item) {
val = header.field[i];
found = true;
break;
}
}
if (found && val != null) {
val[srcServ.KEY_DESC_VALUE] = itemVal[item];
details.push(val);
}
}
obj.details = details;
counter--;
if (counter == 0) {
$scope.showMoreDetails = true;
$scope.beList.beItems = tempItems;
console.log(JSON.stringify($scope.beList));
}
tempItems.push(obj)
})(j);
// $scope.beList.beItems.push(obj);
}
}
}
}
}
}
}
}
}

Categories