Why is altered value being written to original array onclick - javascript

i want to pull data from a .csv file and pass this data to a function to format every row of the .csv file to be a single object. These objects are then stored in an array called "list". So far so good. This is all working.
Next i want a button that calls a function onclick called "roll" that takes a random index from the "list" array and saves an instance of the random oject in a temporarily variable called randomItem.
Then i want to check certain properties of randomItem for specific values and if a certain condition is met it should change a specific property called "randomItem.Name". Finally i want the altered "randomItem" to be pushed into a new array called "results". These results are then being displayed on the website.
If I change the propertys value with "randomItem.Name = randomItem.Name + 'someString'" it also overwrites the original object in the "list" array. I dont want it to do this as i want to repeat the process of rolling random objects from this list several times. Therefor i need the "list" array to keep its original state. I cant get my head around why it overwrites the any list.
html
<button id="btnItem1">Roll</button>
js
$(document).ready(function() {
let list = [];
let results = [];
$('#btnItem1').click({slotName:'item1', listName:list, index:0}, roll);
// i need to pass slotName, listName and index because i have several buttons and list in the real project
getData('data.csv').then(data => formatData(data)); // get data from csv files
async function getData(url) {
const response = await fetch(url);
const data = await response.text();
return data;
};
function formatData(data) { // real formatting function taken out for stackflow
let formatted = // some formatting stuff;
list.push(formatted);
};
function roll(options) {
const slot = options.data.slotName;
const listTemp = options.data.listName;
const index = options.data.index;
if (slot.includes('item')) { // real criteria taken out for stackflow
do {
const randomNumber = Math.floor(Math.random() * (listTemp.length - 1) + 1);
let randomItem = listTemp[randomNumber];
if (1 == 1) { // real criteria taken out for stackflow
let chance = Math.round(Math.random());
if (chance) {
randomItem.Name += '(altered)';
}
}
results.splice(index, 1, randomItem);
} while (1 != 1); // real criteria taken out for stackflow
};
};
});
I expect it to write the altered "randomItem.Name" only to randomItem itself. Not even to listTemp but definitly not to the global "list". Do you have any idea why it is doing this and how i can prevent this? How can i get the object into randomItem without randomItem keeping its reference to any list. Thank you guys in advance!

Related

Compare user input with csv file, return second column value javascript

I came across the following topic, it just has 1 line instead of 2 columns.
How do I return the second value here (see topic below)
Compare my variable with a csv file and get the matching value in javascript
This is my CSV file values:
csv screenshot of columns
This is what I have currently
IT just checks the file for the serial number from the user and marks the div with text "Valid".
This Valid should have the second Columns value.
<script>
const checkm = document.getElementById('check');
checkm.addEventListener('click', serialChecker)
async function serialChecker(){
const url = 'http://localhost/validator/serials.csv';
const response = await fetch(url);
// wait for the request to be completed
const serialdata = await response.text();
console.log(serialdata);
const inputserialnumber = document.getElementById('serialnumber').value.toString();
console.log(inputserialnumber);
// serialdata.match(/inputserialnumber/)
// serialdata.includes(inputserialnumber)
if(serialdata.includes(inputserialnumber) == true && inputserialnumber.length == 7 ){
document.getElementById('validity').innerHTML = "Valid";
startConfetti(); // from confetti.js
}else {
document.getElementById('validity').innerHTML = "Invalid";
stopConfetti(); // from confetti.js
}
//document.getElementById('test').innerHTML = "Valid";
}
</script>
This is my console output
It shows the full csv(currently), & the users input
changed the csv data into to different arrays if that helps:
array
& Thanks all in advance for taking the time to reply to my silly question!
EXTRA Clarification:
What I'm trying to do is a validate website checker.
So the user inputs their serial through an simple input field. & I have the serials in a csv file with an extra column that has the name matching to the serial.
So if the user inputs 1234567 it is present in the CSV file, my current code returns value = true for that. as it is present in the CSV file.
But I want it to return the value next to 1234567 (so in the second Column) instead, in this case "test1". So I can use that value instead of just a standard "Valid" text to be pushed back onto the website.
You can match values of two arrays by their index. In your case, I think it's easiest to use Array.map() to return a transformed array based on the one you loop trough. So for example, if you have two arrays called namesArray and valuesArray, do the following:
const validationResults = valuesArray.map((value, index) => {
return {
valid: checkValidity(value), // or whatever your validation function is called
name: namesArray[index] // match the index of the namesArray with the index of this one (valuesArray)
};
// or `return namesArray[index] + ', valid: ' + checkValidity(value)`
});
This loops through the valuesArray, and validationResults will then be an array of what you return per each item in the map function above.
One important note is that this assumes the arrays are both in the same order . If you want to sort them, for instance, do this after this.
Looking up and registering the values in a Map seems like the best answer.
// ...
const serialdata = await response.text();
const seriallookup = new Map();
// Set all Serial values to Names
for (let s in serialdata.split("\n")) {
let data = s.split(',');
seriallookup.set(data[0], data[1]);
}
Using this, checking for a serial's existance could be done with .has()
if (inputserialnumber.length == 7 && seriallookup.has(inputserialnumber)) {
And set to the elements text using
document.getElementById('validity').innerHTML = serialdata.get(inputserialnumber);
If the .csv file most likely wouldn't change between multiple requests (or if you only send just one request), you should probably initialize and request the data outside of the function.
Thank you all for the feedback.
I have not been able to use your suggestions exactly as intended.
But I managed to combine the idea's and create a new piece that does the trick for me!
const checkm = document.getElementById('check');
checkm.addEventListener('click', serialChecker)
async function serialChecker(){
const url = 'http://localhost/validator2/naamloos.csv';
const response = await fetch(url);
// wait for the request to be completed
const serialdata = await response.text();
const table = serialdata.split('\r\n');
const serialsArray = [];
const nameArray = [];
table.forEach(row =>{
const column = row.split(',');
const sArray = column[0];
const nArray = column[1];
serialsArray.push(sArray);
nameArray.push(nArray);
})
var array1 = serialsArray,
array2 = nameArray,
result = [],
i, l = Math.min(array1.length, array2.length);
for (i = 0; i < l; i++) {
result.push(array1[i], array2[i]);
}
result.push(...array1.slice(l), ...array2.slice(l));
function testfunction(array, variable){
var varindex = array.indexOf(variable)
return array[varindex+1]
}
//calling the function + userinput for serial
const inputserialnumber = document.getElementById('serialnumber').value.toString();
console.log(testfunction(result, inputserialnumber))
if(serialsArray.includes(inputserialnumber) == true && inputserialnumber.length == 7 ){
document.getElementById('validity').innerHTML = "Valid " + testfunction(result, inputserialnumber);
startConfetti();
}else {
document.getElementById('validity').innerHTML = "Invalid";
stopConfetti();
}
}
Hope this can help someone out in having an input field on their website with a .csv file in the backend (possible to have multiple for the user to select with a dropdown box with the async function).
This will check the file & will return the value from the csv that matches the serial!(based on serial number & length of the serial number(7characters))

Is there a way where i can get information from an Object within an Array of an API with a function so that i can show it on the HTML?

I'm trying to get random info from the API of Jojo's. I want to select specific parts of the array object that I get from the API so that I show only the useful information that it provides from the characters. But I can't manage to get the info of the part from "stand_type" of any item in the array, please someone helps me with this.
My Code so far:
async function getCharacter() {
const number = getRandomNumber();
var infos = await fetch('https://jojoapi.herokuapp.com/DiamondIsUnbreakable').then(response => response.json());
console.log("Number = " + number);
console.log(infos[number])
}
infos[number] is accessing the object of result with index is the random number. So you need to access the particular key by passing the key name like
infos[number]["stand_type"]
infos[number].stand_type
And I hope, that generating a random number function of your gives a number less than the array length. I modified the code a bit and it works as expected(I guess)
async function getCharacter() {
var url = 'https://jojoapi.herokuapp.com/DiamondIsUnbreakable';
var infos = await fetch(url).then(response => response.json());
const number = Math.floor(Math.random()*infos.length);
console.log("Number = " + number);
console.log(infos[number]["stand_type"])
}

getting confused with trying to insert nesting objects

Currently trying to build a javascript form that converts the inputs into JSON. I've managed to create objects that allow multiple keys/values to be entered however I'm struggling to get my head around the logic and code for nesting a child object within an object.
let objects = [];
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('btn2').addEventListener('click', function(e) {
e.preventDefault();
let infoBoxOne = document.getElementById('key').value // store the key into a variable
let infoBoxTwo = document.getElementById('value').value // store the value into a variable
const lastObject = objects[objects.length-1] // finds the last object in the objects array
const objectValues = Object.entries(lastObject) // gets all the keys and values
const lastKeyValuePair = values[values.length-1]; // stores the last key and value entered into a variable
})
})
So my initial idea was to find the last key/value within the last object that was added and use something like Object.create() or push() to insert the data. Is there an easier way of achieving this?
edit: here's a jsfiddle showing what I have exactly so far https://jsfiddle.net/9jrzLxnm/
Secone edit: idea of what I'm trying to achieve
{
{
"firstObject":'FirstObject'
},
{
"lastObject": {
"infoBoxOne": "JlastObject",
},
}
}
Ok firstly you have to set the desired location to place a child object as an object itself.. then the rest is intuitive
My example is on jsfiddle
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('btn2').addEventListener('click', function(e) {
e.preventDefault();
let childKey = document.getElementById('key').value // store the key into a variable
let childValue = document.getElementById('value').value // store the value into a variable
const lastObject = objects[objects.length-1] // finds the last object in the objects array
const values = Object.entries(lastObject) // gets all the keys and values
const [key,value] = values[values.length-1]; // stores the last key and value entered into a variable
lastObject[key]={} //turning place to put child into an object
lastObject[key][childKey]=childValue //placing the nested child
document.forms[0].reset();
listToJson();
})
})

What is a faster way to write this function to delete rows/objects in a table?

So, I have this function that, after an update, deletes elements from a table. The function, lets call it foo(), takes in one parameter.
foo(obj);
This object obj, has a subfield within called messages of type Array. So, it would appear something like this:
obj.messages = [...];
Additionally, inside of obj.messages, each element contains an object that has another subfield called id. So, this looks something like:
obj.messages = [{to:"You",from:"Me",id:"QWERTY12345.v1"}, ...];
Now, in addition to the parameter, I have a live table that is also being referenced by the function foo. It uses a dataTable element that I called oTable. I then grab the rows of oTable and copy them into an Array called theCurrentTable.
var theCurrentTable = oTable.$('tr').slice(0);
Now, where it gets tricky, is when I look into the Array theCurrentTable, I returned values appear like this.
theCurrentTable = ["tr#messagesTable-item-QWERTY12345_v1", ...];
The loop below shows how I tried to show the problem. While it works (seemingly), the function itself can have over 1000 messages, and this is an extremely costly function. All it is doing is checking to see if the current displayed table has the elements given in the parameter, and if not a particular element, delete it. How can I better write this function?
var theCurrentTable = oTable.$('tr').slice(0);
var theReceivedMessages = obj.messages.slice(0);
for(var idx = 0; idx < theCurrentTable.length; idx++){ // through display
var displayID = theCurrentTable[idx].id.replace('messagesTable-item-','').replace('_','.');
var deletionPending = true;
for(var x = 0; x < theReceivedMessages.length; x++){
var messageID = theReceivedMessages[x].id;
if(diplayID == messageID){
console.log(displayID+' is safe...');
deletionPending = false;
}
}
if(deletionPending){
oTable.fnDeleteRow(idx);
}
}
I think I understand your problem. Your <tr> elements have an id that should match an item id within your messages.
First you should extract the message id values you need from the obj parameter
var ids = obj.messages.map(function (m) { return '#messagesTable-item-' + m.id; });
This will give you all the rows ids you need to keep and then join the array together to use jQuery to select the rows you don't want and remove them.
$('tr').not(ids.join(',')).remove();
Note: The Array.prototype.map() function is only supported from IE9 so you may need to use jQuery.map().
You could create a Set of the message ID values you have, so you can later detect if a given ID is in this Set in constant time.
Here is how that would look:
var theCurrentTable = oTable.$('tr').slice(0);
var theReceivedMessages = obj.messages.slice(0);
// Pre-processing: create a set of message id values:
var ids = new Set(theReceivedMessages.map( msg => msg.id ));
theCurrentTable.forEach(function (row, idx) { // through display
var displayID = row.id.replace('messagesTable-item-','').replace('_','.');
// Now you can skip the inner loop and just test whether the Set has the ID:
if(!ids.has(displayId)) {
oTable.fnDeleteRow(idx);
}
});
So now the time complexity is not any more O(n.m) -- where n is number of messages, and m the number of table rows -- but O(n+m), which for large values of n and m can make quite a difference.
Notes:
If theCurrentTable is not a true Array, then you might need to use a for loop like you did, or else use Array.from(theCurrentTable, function ...)
Secondly, the implementation of oTable.fnDeleteRow might be that you need to delete the last rows first, so that idx still points to the original row number. In that case you should reverse the loop, starting from the end.

Get all items in NotesXSPDocument

In my Notes Database, I perform an audit when the document is saved. Pretty easy in LotusScript. I grab the original document (oDoc) from the server, then in the document I modified (mDoc), I do a Forall loop that gets the names of each item; forall item in mDoc.items. Grab the same item from oDoc, execute a function with the new item as an argument that will run down a case statement that will see if its a field we care about. if so, I update a set of list values in the document with "When", "Who", "What field", and the "New Value".
I'm doing this in a server side script. In trying this, I discovered a couple of interesting things;
currentDocument is the NotesXSPDocument that contains everything that was just changed.
currentDocument.getDocument() contains the pre-change values. It also returns a NotesDocument which has the "items" field that I can run through.
Thing is, I need something similar in the NotesXSPDocument. Is there a way in an iterative loop to grab the names and values of all items from there?
Here's the broken code. (Currently it's walking through the NotesDocument items, but those are the old values. I'd rather walk down the XSP document items)
function FInvoice_beginAudit() {
var original_doc:NotesDocument = currentDocument.getDocument();
var oItem:NotesItem;
var oItems:java.util.Vector = original_doc.getItems();
var iterator = oItems.iterator();
while (iterator.hasNext()) {
var oItem:NotesItem = iterator.next();
item = currentDocument.getItemValue(oItem.getName());
if (oItem == undefined) {
var MasterItem = ScreenAudit(doc,item,True)
if (MasterItem) { return true }
} else {
if (item.getValueString() != oItem.getValueString()) {
var MasterItem = ScreenAudit(doc,Item,True);
if (MasterItem) { return true }
}
}
}
}
You can get both versions of a document after submit - the original and the one with changed/new values:
original: var original_doc:NotesDocument = currentDocument.getDocument();
changed: var changed_doc:NotesDocument = currentDocument.getDocument(true);
This way you can compare the items for changes.
But, there is a pitfall: after assigning "changed_doc" to currentDocument.getDocument(true) the "original_doc" has the changed values too because both variables point to the same document. That's why we have to copy all items from currentDocument.getDocument() to a new temporary document first and only after get the changed values with currentDocument.getDocument(true). As an alternative you could read the original document from server like you do in LotusScript.
This is a code for detecting changed items as a starting point:
var original_doc:NotesDocument = database.createDocument();
currentDocument.getDocument().copyAllItems(original_doc, true);
var changed_doc:NotesDocument = currentDocument.getDocument(true);
var oItems:java.util.Vector = original_doc.getItems();
var iterator = oItems.iterator();
while (iterator.hasNext()) {
var oItem:NotesItem = iterator.next();
var itemName = oItem.getName();
var cItem:NotesItem = changed_doc.getFirstItem(itemName);
if (cItem.getText() !== oItem.getText()) {
print("changed: " + itemName);
}
oItem.recycle();
cItem.recycle();
}
original_doc.remove(true);
original_doc.recycle();

Categories