Splice removes only last element in array - javascript

I'm having a small issue with React (still new to it). I have an array of Results. These Results have nested Bookings, also in an array, and the latter is what I'm manipulating.
When User creates Booking, everything goes as expected - findIndex gets the correct Result element and modifies its Bookings array accordingly.
However, when I want to "Unbook", it only finds the last Result in the array, and findIndex is always -1 (so I haven't even gotten to the Bookings part, because the Result index I get is wrong).
The code is similar, my items all have unique keys, and I don't understand what could be the problem (using Alt as Flux implementation)?
Here is what happens on Create:
onCreateBookingSuccess(data) {
let resultIndex = this.results.findIndex((result) => result.id === data.id);
this.results.update(resultIndex, (result) => result.bookings.push(data));
toastr.info('Booked! User will receive notification.');
}
And on delete:
onDestroyBookingSuccess(data) {
let resultIndex = this.results.findIndex((result) => result.id === data.id);
var myBooking;
this.results.map((result) => {
myBooking = result.bookings.findIndex((booking) => booking.id === data.booking);
});
this.results.update(resultIndex, (result) => result.bookings.splice(myBooking,1));
toastr.warning('Unbooked! User will receive notification.');
}
My object:
<Result key={result.id} id={result.id} bookings={result.bookings} />
As I mentioned, the first operation goes as planned, everything is modified as it should. The issue with the second op starts from the very beginning, when resultIndex returns -1.

The problem seems to be here:
var myBooking;
this.results.map((result) => {
myBooking = result.bookings.findIndex((booking) => booking.id === data.booking);
});
You’re always assigning to myBooking, even when the index is not found (-1) after having already found it, so it’s equivalent to this.results.last().bookings.findIndex(...). Really you only want to get the (first?) value that’s not -1:
var myBooking = this.results.map((result) => {
myBooking = result.bookings.findIndex((booking) => booking.id === data.booking);
}).find((index) => index != -1);
Also, consider renaming myBooking to better indicate it’s an index and not the actual record.

Related

'splice' instead of 'filter'

I have a field for entering tags. It is also possible to remove tags.
And here I have a question.
My deletion code is below. Tags can be deleted one at a time, in any order (first, last, somewhere in the middle, it doesn't matter).
const deleteTag = (index) => {
setTags((prevState) => prevState.filter((tag, i) => i !== index));
};
But I would like to use 'splice' instead of 'filter'. With the same functionality.
Tell me how to do it
Splice's first argument is the item index, and the second argument is how many items you want to delete from that index.
const deleteTag = (index) => {
setTags((prevState) => {
prevState.splice(index, 1)
return [...prevState]
});
};
UPDATE
If you are using <StrictMode> the setState will execute twice and will delete two items instead of one.

React Native array.map won't sum a state

i'm working on a corona data app for school and I want to sum the amount of tests and positive results from an API.
Here is my code:
function GetDataApi() {
if(data !== null) {
const filter = data?.filter((item => item.Date_of_statistics === '2021-04-01'));
filter.forEach((item, index) => {
setUseTestedResult(parseInt(useTestedResult) + parseInt(item.Tested_with_result));
setUsePositiveData(parseInt(usePositiveData) + parseInt(item.Tested_positive));
});
}
}
It only returns the numbers of the last object in the array. It looks like the setState function isn't working.
State updates are asynchronous. You're repeatedly updating your state, overwriting previous updates, so you end up storing only the result of the last call to your state setters.
Instead, build the sum, then set the result:
function GetDataApi() {
if (data !== null) {
// No ? here −−−−−−−−−−−−v
const filteredData = data.filter((item => item.Date_of_statistics === '2021-04-01'));
let testedSum = 0;
let positiveSum = 0;
for (const {Tested_with_result, Tested_positive} of filteredData) {
testedSum += Tested_with_result;
positiveSum += Tested_positive;
}
setUseTestedResult(previousValue => previousValue + testedSum);
setUsePositiveData(previousValue => previousValue + positiveSum);
}
}
A couple of notes:
I've removed the parseInts, hopefully the data you're using is already numeric, but add them back if you need them. Only parse strings, don't call parseInt on numbers.
Note that I replaced the optional chaining (?.) with normal chaining (.), since you've just tested that data isn't null and if it's undefined, your subsequent code would fail anyway.
Your code was adding the sum from data's entries to the existing state member's value, so that's what I've done above, using the callback form of the state setters (since any time you use existing state to set new state, you should use the callback form). But to me it seems a bit odd to be adding this information to an existing state member, rather than replacing it.

Trying to build remove function, going wrong

I am trying to build a remove function to my weather app, the function working only on the last place in the array I tried via MAP loop but I get too many renders errors.
This is the sandbox URL of my project -
https://codesandbox.io/s/kind-platform-cymzx?fontsize=14&hidenavigation=1&theme=dark
I don't get it if ts working on the last one why won't you on the first one?
(with for loop it's letting me edit only the last one, in map loop, it's saying too many renders)
Guys I stuck on this for 3 days now please help.
Here is your function:
let deleteFunc = (name) => {
allFavorite.map((item, index) => {
console.log(item.name);
let updatedFavorite = allFavorite.filter((item, i) => (item.name = name));
setAllFavorite(updatedFavorite);
});
};
First problem is that you mutate item.name in filter function.
Second problem is that you are seting state inside the map, so you call setAllFavorite for each item in your allFavorite.
Your goal here is to construct new state first, and then apply it in one go, like this:
let deleteFunc = (name) => {
// this will return new array where item with name `name` removed, and will pass it into state setter
setAllFavorite(allFavorite.filter((item) => item.name !== name));
};

Updating components based on sorted array. Angular (4.3)

Working with an array of data that we want to be able to sort for display in a component, and it doesn't seem to be sorting or updating the DOM, however I have a working code sample that properly demonstrates the concept, and it should be sorting, but in the angular app, it's simply not getting sorted.
The parent component that houses the original data stores the data on an Input parameter object called Batch, and the array we're sorting is on Batch.Invoices.Results. The event from the child component is fine, and the appropriate data is confirmed to bubble to the parent component.
The function that's supposed to sort the array looks like this:
public OnInvoiceSortChange({orderValue, orderAscending}){
console.log(`Invoice Sorting has been called. Value: ${orderValue} . Ascending? ${orderAscending}`);
console.log(`Before:`);
console.log(this.BatchViewModel.Invoices.Results.map(x => x.VendorName));
const sortingArray = [...this.BatchViewModel.Invoices.Results];
if(orderAscending){
const sorted = sortingArray.sort((a, b) => a[orderValue] > b[orderValue] ? 1 : 0);
this.BatchViewModel.Invoices.Results = sorted;
console.log('Sorted');
console.log(sorted.map(x => x.VendorName));
} else {
const sorted = sortingArray.sort((a, b) => a[orderValue] < b[orderValue] ? 1 : 0);
this.BatchViewModel.Invoices.Results = sorted;
console.log(sorted.map(x => x.VendorName));
}
console.log(`After:`);
console.log(this.BatchViewModel.Invoices.Results.map(x => x.VendorName));
}
All the console logs are for debugger visibility, and the output is this:
Where in my testing file (non-angular) looks like this:(where data is a direct copy of the array from the Angular app.
const ascendingData = [...data];
const descendingData = [...data];
const sortedDescending = descendingData.sort((a, b) => a['VendorName'] < b['VendorName']? 0 : 1)
const sortedAscending = ascendingData.sort((a, b) => a['VendorName'] > b['VendorName']? 0 : 1);
const vendorListAscending = sortedAscending.map(x => x.VendorName);
const vendorListDescending = sortedDescending.map(x => x.VendorName);
console.log(vendorListDescending);
console.log(vendorListAscending);
and the output looks like this:
So I see that the sorting should work, but it's just not happening in Angular.
How can I get the array sorted, and update the DOM as well?
The function you pass to sort is wrong. It is supposed to return a negative value for "less", a positive value for "greater" or zero for "equal". If orderValue is numeric then it's easiest to just return a[orderValue] - b[orderValue], if not then just change your 0 to -1.
(By the way, name orderKey could be a bit clearer maybe?)
I don't think angular has anything to do here, but I cannot tell now why you get different results. Anyway, your sort function is invalid (it states that a equals b, but at the same time b is greater than a), I hope fixing this function helps.

Steam API, different json format

Recently i integrated CSGO stats in my discord bot, but today i saw that for almost every player the API sends a different json data.
Here 2 examples:
https://jsonblob.com/58688d30-26d0-11e8-b426-7b3214778399
https://jsonblob.com/52ed0c3f-26d0-11e8-b426-43058df4a5a6
My question was how to request the data properly so a win is really a win and not a kill.
.addField('**Wins:**', `${object.playerstats.stats[5].value}`, true)
.addField('**Time played:**', `${object.playerstats.stats[2].value}` + ' minutes', true)
.addField('**Kills:**', `${object.playerstats.stats[0].value}`, true)
.addField('**Deaths:**', `${object.playerstats.stats[1].value}`, true)
.addField('**Bombs planted:**',`${object.playerstats.stats[3].value}`, true)
.addField('**Money earned:**',`${object.playerstats.stats[7].value}`, true)
.addField('**Knife kills:**',`${object.playerstats.stats[9].value}`, true)
.addField('**Headshot kills:**',`${object.playerstats.stats[24].value}`, true)
.addField('**Dominations:**',`${object.playerstats.stats[39].value}`, true)
.addField('**Rounds played:**',`${object.playerstats.stats[44].value}`, true)
The name property of stats items appear to be unique enough to find. You can use array.find to look for the correct stat by name.
const stats = object.playerstats.stats
const totalKills = stats.find(s => s.name === 'total_kills').value
const totalDeaths = stats.find(s => s.name === 'total_deaths').value
Taking it further, you can use array.reduce to generate an object whose key is name and value is value for each item in the array. This way, you access it like an object.
const stats = object.playerstats.stats
const statsObj = stats.reduce((c, e) => (c[e.name] = e.value, c), {})
const totalKills = statsObj.total_kills
const totalDeaths = statsObj.total_deaths
Rather than trying to reference the array indexes, why not convert the API response into an easier-to-parse format?
// do this once...
let playerStats = {};
object.playerstats.stats.forEach(s => playerStats[s.name] = s.value);
// ...then you can use the playerStats variable however you need:
.addField('**Kills:**', `${playerStats.total_kills}`, true)
.addField('**Wins:**', `${playerStats.total_wins}`, true)
The stats array is just not sorted. you can use .find() to get the correct entry from the stats.
for example
const totalWins = object.playerstats.stats.find(stat => {
return stat.name === 'total_wins';
});
.addField('**Wins:**', `${totalWins.value}`, true)
You are approching this problem the wrong way.
JSON is not a format that is ordered. What that means is that there is no guarantee that the JSON data will return in the same order everytime. It is not a default of the API.
There is one way you could still use your way: by sorting the 'stats' array by name. but it is a long operation and not a very good idea.
The way do to this is to do a lookup by name.
For example, if you want to find the wins, you do this :
object.playerstats.stats.find(elem => elem.name === 'total_wins').value;
The find function does a lookup and returns the first element matching the predicate (elem.name === 'total_wins'). It returns null if not element matched the predicate (so be careful here).
You could do a function that returns a value for you :
findValue(statsArray, name) {
const entry = statsArray.find(elem => elem.name === name);
return entry ? entry.value : '?';
}
And then your code would look like this :
...
.addField('**Wins:**', findValue(object.playerstats.stats, 'total_wins'), true)
...
The main thing here is : never assume fields in a JSON will return the same every time. Always use lookup, and not indexes (unless it is sorted).

Categories

Resources