Okay guys so I've run into a problem on some code I've been working on. In the code below, I'm trying to console log the length of an array, but after printing to the console, I get an output of 0 for the array's length, and when I print the array itself, it shows that it is empty, but I can click the drop down arrow and see the element inside it.
// randomly selects card from array
export const selectCard = arr => {
const randomArr = Math.floor(Math.random() * arr.length);
console.log(arr.length);
console.log(arr);
}
Image of logged array length and empty array with an element inside.
To test what was happening, I decided to console log the array in a different function that is in the same file where the array is created. The array (blackCards) is stored in an object.
let packArr = {
data: {},
packToUse: [],
whiteCards: [],
blackCards: [],
};
Console logging the array (blackCards) in another function logs this:
const seperatePacks = (data) => {
// add white cards to array
packArr.whiteCards.push(data.whiteCards);
// add black cards to array
packArr.blackCards.push(data.blackCards);
// console log blackCards array
console.log(packArr.blackCards);
};
Image of logged array from a different function which is within the same file where the array is created and stored.
Here's a little more about how my code works. When a user clicks on a button, an event listener is activated, which calls other functions that eventually get json from a sever, then I add that data to the array, which you see in the code above. So now that you know how the code works, I can get to what is causing me to be even more confused. So, on the first click, I get the outcome of all the previous code and images combined, but if i click on the same button again, or click on a different one, I get the desired outcome (if I click on two button I get two elements in the array, so it's working correct). So, two or more clicks and the program works the way it should. Any help here would be great. Also sorry if this post was a little scuffed, it is my first one.
Here are the functions that eventually call selectCard
elements.packs.addEventListener("click", (e) => {
const packID = e.target.closest(".pack").id;
if (packID) {
packsToUse(packID);
// create black cards
blackCardDisplay(packArr.blackCards);
}
});
Above is the listener that was mentioned earlier, which calls the function below, that will call selectCard.
export const blackCardDisplay = data => {
const card = `
<div class="cards card--black">
<h1 class="card--black-text card--text">${selectCard(data)}</h1>
</div>
`;
}
Here is the output after pressing two buttons. The array should contain two elements but in this case it only shows one. However when I expand the array, it shows a length of two instead of just one.
Console image of two button clicks
EDIT: After reading some of the comments I realized I forgot to mention that the contents of blackCards is an array. So I have an array within an array.
So, after reading some comments I decided to implement #charlietfl's comment that maybe the array was console logged before the ajax call had been completed. Although this did not explicitly fix the problem, it set me on the right track to the solution.
Here is how I solved this problem:
I took the comments given about the console log completing before the ajax call could finish, and from there, I did some research and found this very simple article:
https://www.programmersought.com/article/26802083505/
In this article was an example using the setTimeout() method.
Here is the final solution to my problem.
export const selectCard = arr => {
const randomArr = Math.floor(Math.random() * arr.length);
setTimeout(() => {
console.log(arr.length);
console.log(arr)
}, 1000);
}
Here is the output after adding the setTimeout() method on the first button press.
Image of solved array length and array console log
I'm not aware if this is the best solution for the problem, because I've seen some people disapprove of using this method in the past, but it will work fine for what I'm doing at this point. If anyone has a better way of solving this I would love to hear it!
Related
I'm trying to use infinite scroll by doing this:
And it's working properly. However, when I pass the data to the stickers state and then do the mapping, the first items of the call are duplicated. Example:
Note that the gifs are duplicated. However, infinite scroll is working correctly. When it runs, it makes a new call with new gifs and adds them to the screen without repeating them. Only these early state firsts start out duplicates. The code is like this:
Does anyone know how I can solve this problem?
It looks like every gif has an id so you just need a way to filter out all those new items where their id doesn't already exist in the current array of items.
// Accept the current array of gifs, and a new array of gifs
function dedupeGifs(gifs, newGifs) {
// `map` over the current array of gifs
// to create an array of ids
const gifsIds = gifs.map(gif => gif.id);
// `filter` out all those gifs that don't
// already exist
return newGifs.filter(gif => {
return !gifsIds.includes(gif.id);
});
}
Then merge the current array, and the deduped array together:
const dedupedItems = dedupeGifs(gifs, newGifs);
setState(prev, [ ...prev, ...dedupedGifs ]);
In my React app, at one point I'm trying to copy a Map from an object, but it's not working for me.
The initial user object has the structure:
user: {
videoTracks: Map,
... other fields...
}
If I do a console log on user, I get this for videoTracks:
Then I do this: const newVideoTracks = user.videoTracks;
and I would expect newVideoTracks to be the same as the first image, but instead I get the following:
It has 2 entries and size:2, but it says Map(1) at the top.
I know the console isn't always accurate, but in this case when I subsequently use newVideoTracks, it behaves as though there is only 1 entry.
I assume I need to do some cloning rather than just copying it, so I tried both of the following:
const newVideoTracks = new Map(user.videoTracks); - this didn't make any difference
const newVideoTracks = Object.assign({}, user.videoTracks) - caused a crash
What's the correct way to do this?
I am learning React and just created a simple todo app using only React. My todo app has the standard structure of having a text input and an "ADD" button next to it. The user would type their todo in the input and every time they click on the "ADD" button next to it, a new ordered list of their inputs would appear underneath the input and "ADD" button.
The user can also delete a todo entry by clicking on the entries individually, like this:
To accomplish this behaviour of deleting entries, I used this delete function:
delete(elem) {
for (var i = 0; i < this.state.listArray.length; i++) {
if (this.state.listArray[i] === elem) {
this.state.listArray.splice(i, 1);
this.setState({
listArray: this.state.listArray
});
break;
}
}
}
My todo app works exactly the way that I want it to work, but as I look at other people's more conventional approach to this delete function, they either just simply use the splice method or the filter method.
For the splice method approach, they apparently just simply "remove" the unwanted entry from the listArray when the user clicks the particular entry. This does not work for me as using this method results in all my entries getting deleted except for the entry that I clicked on, which is the one that I want to delete.
On the other hand, the filter method approach apparently works by comparing the elem, which is the data passed from a child component, with each element in the listArray, and if the element in the for loop does not equal to the elem, then it would be passed onto a new array. This new array would be the one to not be deleted. This approach works better than the simple splice approach, however, one problem that I had encountered with this approach is that if I have more than one entry of the same value, for example, "Feed the dog". I only want one of the "Feed the dog" entries to be deleted, but it deletes both of them.
I thought of an approach to tackle this problem, eventually coming up with the current version of my code, which uses the splice method, but the splice method is used before I set it in the state. As evident here:
this.state.listArray.splice(i, 1);
this.setState({
listArray: this.state.listArray
});
My question can be broken down into three subquestions:
Considering that React states should be immutable, is the first line of the code above mutating my state? Is this approach not okay?
I thought that all React states were only possible to be changed inside a "setState" function, but my first line of code from above is not inside a setState function, yet it changed the state of listArray. How is this possible?
If my approach is mutating the state and is not ideal, how would you go about making the delete function so that it only deletes one entry and not more than one if there are multiple similar entries?
Yes, splice affects the array it acts on so don't use in this way. Instead you need to create a new array of the correct elements:
this.setState({
listArray: this.state.listArray.filter((el, idx) => idx !== i);
});
If you want to remove only the first instance, maybe couple with a findIndex (although indexOf would work in your example as well) first:
delete(elem) {
const idxToFilter = this.state.listArray.findIndex(el => el === elem);
if (idxToFilter < 0) {
return;
}
this.setState({
listArray: this.state.listArray.filter((el, idx) => idx !== idxToFilter);
});
}
This creates a new array without modifying the old which will cause anything that reacts to listArray changing to be notified since the reference has changed.
I am importing an excel file using xlsx in angular, where the excel file is imported as an object of arrays, with each array being a row of the excel file and each item in each array is a cell within it's respective row. See below for how this is done
onFileChange(event: any)
{
const inputFile: DataTransfer = <DataTransfer>(event.target);
const fileReader: FileReader = new FileReader();
fileReader.onload = (event: any) =>
{
const binaryString: string = event.target.result;
const workBook: XLSX.WorkBook = XLSX.read(binaryString, { type: 'binary', sheetStubs: true});
/* sheetstubs true supposedly shows empty cells but isn't */
const workSheetName: string = workBook.SheetNames[0];
const workSheet: XLSX.WorkSheet = workBook.Sheets[workSheetName];
this.data = <Array>(XLSX.utils.sheet_to_json(workSheet,
{header: 1, blankrows: true }));
};
I am now trying to find the column which contains a manufacturer description by using a Regex search by looping through each of the arrays and searching for the term cap like so:
getManufacturerDescriptionColumn()
{
console.log(this.data)
for (const row in this.data)
{
var manDescriptIndex = row.search(/cap/i)
console.log(manDescriptIndex)
if (manDescriptIndex > -1)
{
return manDescriptIndex
}
}
}
however when ever I try this, even though the phrase cap is clearly present in some of the arrays when I run this, I am presented with all -1 values indicating that the phrase is not found in any of the arrays. Below is an attached example where CAP is clearly present in a couple of the 15 rows of the excel file but I am still met with 15 -1 values.
For example the array indexed at
Any ideas why a regex search isn't identifying the phrase in this scenario when I do console.log(this.data) I can see the phrase cap like so,
I have also tried adding another layer of iteration to isolate the strings of the individual cells in the row also to no avail.
As you can clearly read here MDN you are using the loop for..in which is a mistake, and you should be using for..of instead. Explanation below
for..in is made to iterate around objects, which actually works for arrays, but is just giving you the indexes, which in this case will be 0,1,2 and so on...
If you want to get the values you will have to use the for..of loop MDN
You will see this more clearly if you print console.log(row) before the check
Ok so #AlejandroVales was definitely right in his answer so I'm not gonna take away that well earned check mark but... I have an update to fully get the solution because while changing in to of was instrumental to me getting to the point I needed to get, there was more to be done and in the spirit of sharing information and frankly good karma, I have for others who run into a similar issue... a solution. Rather than trying to use array methods in order to see if I could find what I am looking for I tested each element in each array AGAINST a regular expression, AKA I flipped the problem on it's head a little bit. So to clarify take a look at the code block below
var cap = new RegExp(/cap/i)
for (const row of this.data)
{
for (let cell = 0; cell<row.length; cell++)
{
if (cap.test(row[cell]))
{
return cell
}
}
}
First set a variable equal to a regex, then iterate to a level where I am iterating over each cell in each array (each array is a row of the excel document in this case), then find the cell that returns a TRUE value and poof problem solved. If I didn't do a good job explaining this solution feel free to leave a comment below I am no expert but figured it's only right try my best to give value to a community that has given me so much value previously.
I have defined a function called Node which stores the properties of nodes in a graph data structure. The function is something like this:
function Node(){
...
this.outEdges = [];
this.inEdges = [];
...
}
where the inEdges and outEdges store elements of type Edge which is another function I have defined. During the program these arrays are filled with elements.
At some point in my code I need to reset these two arrays so I write:
nodes[i].outEdges.length = 0;
nodes[i].inEdges.length = 0;
where nodes is an array of elements of type Node and I am accessing an element in a for loop.
The problem is, after setting outEdges and inEdges to 0, I expected them to be [] in the nodes[i] property list. However, when I output nodes[i] into console, the outEdges and inEdges still have the elements in them. The stranger thing is that when I output nodes[i].outEdges to console, it prints [] , which is correct, but clicking on [ ] again opens the list of the elements! I can't really figure out why the nodes[i] variables don't change?
That happens (probably) because the browser prints out the empty array but by the time you check it, it has content again. So when you click to expand the browser shows the actual content.
As you can see the values [1,3,7] were added after the command console.log(o) but they are shown on the screen (even though the length shown is 0).
You're not supposed to set the length field. Just re-initialize them:
nodes[i].outEdges = [];
nodes[i].inEdges = [];
Edit: My bad, setting the length should work. It does work for me on Chrome at least. However, I still think it's safer and better style to re-init.
Just create a new object with the same name
nodes[i].outEdges = new Array();