how to call post api recursively untill array.length condition is false - javascript

Here is my question how to call recusive post api function untill array.length condition is false
Here is my code what i tried
const postItemArray =["<eg:cat>"]
var collectionDataContainer = [];
const recurseFunction = i => {
const [loadingStatus, mainData, addDataToPostItemArray] = useFetch(`http//blabla${postItemArray[i]}variety`);
console.log(edgeData[i]);
if (i < postItemArray.length) {
recurseFunction(i + 1);
}
if (addDataToPostItemArray.length !== 0) {
postItemArray.push(...addDataToPostItemArray);
}
if (mainData.length !== 0) {
collectionDataContainer.push(...demoData);
}
console.log(DummyArray);
};
recurseFunction(0);
here the parameters in useTetch are
loadingStatus will give true or false condition
mainData will give Data after fetching
addDataToPostItemArray fitered out mainData and will give array of keys if MainData have any keys for next loop recursion
Here is the Example Data format for understanding before calling recursive function
step-1
initially postItemArray = ["<eg:cat>"]
after passing postItemArray[i] value to useFetch then we will get data like this
mainData=[
{key : "<eg:dog>",class : "animal"},
{key : "<eg:ant>",class : "insect"},
{key : "<eg:monkey>", category : "jumping"},
{key : "<eg:fish>", category : "swim"}
]
step-2 addDataToPostItemArray this array will find out the class object keys if mainData have class key value pair inside the useFetch and collect the key in the format of below
addDataToPostItemArray =["<eg:dog>","<eg:ant>"]
if mainData array object have class Key it will catch and element store the keys inside the addDataToPostItemArray
here is the logic done in useFetch file for finding keys
axios(config).then(response => {
let originalResult = response.data.results.bindings
.filter(ek => ek.Class !== undefined && ek)
.map(el => el.key);
setFindClass(originalResult);
but my problem is im able to get the keys from useFetch and pushed into the postItemArray
and postItemArray length will exceeds to 3 as we expected
but in inside the recurseFuction if statement(i < postItemArray.length) condition looping only one time because its cosidering same initial length after pushed some data to postItemArray
so that why im not able to get looping data after array length is increased
this is my fail condition
if (i < postItemArray.length) {
recurseFunction(i + 1);
}
please anyone can help me out please im unable to find the solution for this question
just only i want to collect the Data into this container collectionDataContainer = []
looping if mainData have class and key object
if any one have better logic please help me out that is soo appreciatable

Related

How to assign new key name to an object in an array of data?

In a React project, I've certain number of records which also contain input fields like Date and Text components in a Grid. I'm processing the date and text values in save function. All the records are fetched from JSON data, while saving date values its assigned to new key object but new one isn't getting updated with the old one. I have seen many similar posts but, none useful. Please refer to the code below:
Following is the function where I'm extracting both text and date values
const updateGrid = (data) => {
if (Array.isArray(allData) && allData?.length > 0) {
const tempData = allData;
tempData.map(x=> {
if(data.id === x.id) {
x.name = data.textVal
}
// Here I'm assigning the 'Establish' value with 'est'
if(data.id === x.id) {
x['est'] = x['Establish']
x.Establish = data.dateVal
}
})
setDataAll(tempData)
}
console.log('tempdataNew', dataAll)
}
As you can see from above picture, we have two keys, 'est' and 'Establish', but, I need only 'Establish', also getting undefined on 'name' when changed date value and vice-versa. What could be the best optimal solution to tackle this issue?
Please refer to Codesandbox --> https://codesandbox.io/s/jovial-aryabhata-95o2sy?file=/src/Table.js

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.

Delete Session Storage - Shopping Cart Project

I hope this is a good question. I am working on a shopping cart project. I have been scouring the internet through different tutorials on shopping carts. I am attempting to write mine in Vanilla Javascript. I am having a problem with removing shopping cart items from session storage.
Below is what is currently in my session storage. As you can see it is an Array of objects.
[{"id":"8","name":"Candy
Skull","price":"20000","image":"../images/candyskull-
min.JPG","qty":"1"},{"id":"5","name":"Upsidedown
House","price":"20000","image":"../images/upsidedownhouse-
min.JPG","qty":"1"},{"id":"6","name":"Brooklyn
Window","price":"30000","image":"../images/brooklynwindow-
min.JPG","qty":"1"},{"id":"4","name":"Hand That
Feeds","price":"40000","image":"../images/handthatfeeds-
min.JPG","qty":"1"}]
I want to loop through the array and remove the matching object from the cart storage.
Below is the JS Code used to generate the .remove-from-cart buttons. As you can see it includes all the dataset information.
<td>
<span class="remove-from-cart">
<b data-id="${value.id}" data-name="${value.name}" data-
price="${value.price}" data-image="${value.image}" data-
qty="${value.qty}">X</b>
</span>
</td>
To test the functionality of what I have done so far you can visit www.dancruzstudio.com/shop
The function that I can't get to work properly is the removeFromStorage() function. For some reason when comparing an object to the objects in the array I'm never getting back a true boolean value, even when there are items in the cart that should match. Where am I going wrong? I hope someone can help. Below is a copy of my JS code.
The method I am using is having an identical dataset value in the remove item button generated by JS and then parsing that dataset into an object and comparing it to the objects in the session storage array which is called shopItems inside the removeFromStorage() function. I hope this information is enough for someone to see my problem. Thank you in advance.
// Remove item from DOM
document.querySelector('#cart-list').addEventListener('click',
removeFromCart)
function removeFromCart(e) {
if(e.target.parentElement.classList.contains('remove-from-cart')) {
//Remove from DOM
e.target.parentElement.parentElement.parentElement.remove();
//Remove from Session Storage
removeFromStorage(e.target.dataset);
}
}
// remove from Session storage
function removeFromStorage(removedItem){
let shopItems;
if(sessionStorage['sc'] == null){
shopItems = [];
} else {
shopItems = JSON.parse(sessionStorage['sc'].toString());
}
var compare = JSON.parse(JSON.stringify(removedItem))
shopItems.forEach(function(item, index){
if(compare === item){
console.log(compare);
console.log(item);
// shopItems.splice(index, 1);
}
});
sessionStorage['sc'] = JSON.stringify(shopItems);
}
You can not compare objects like this.
let a = {p:1};
let b = {p:1};
console.log(`a ===b ? ${a===b}`);
If your objects are fairly simple you can try comparing their stringify representation:
let a = {p:1};
let b = {p:1};
const compare = (x,y) => {
return JSON.stringify(x) === JSON.stringify(y);
}
console.log(`a === b ? ${compare(a,b)}`);
or write your custom compare function (that may be challenging):
Compare JavaScript objects
Since your objects are decorated with an id, the easisest way would be to idntify them by that:
let storage = [{"id":"8","name":"Candy Skull", "price":"20000","image":"../images/candyskull- min.JPG","qty":"1"},{"id":"5","name":"Upsidedown House","price":"20000","image":"../images/upsidedownhouse- min.JPG","qty":"1"},{"id":"6","name":"Brooklyn Window","price":"30000","image":"../images/brooklynwindow- min.JPG","qty":"1"},{"id":"4","name":"Hand That Feeds","price":"40000","image":"../images/handthatfeeds- min.JPG","qty":"1"}];
let items = [{"id":"6","name":"Brooklyn Window","price":"30000","image":"../images/brooklynwindow- min.JPG","qty":"1"}, {"id":"5","name":"Upsidedown House","price":"20000","image":"../images/upsidedownhouse- min.JPG","qty":"1"}];
const pluck = (acc, crt) => {
acc.push(crt.id);
return acc;
};
let storageIndexes = storage.reduce(pluck, []);
let itemsIndexes = items.reduce(pluck, []);
let removeIndexes = [];
itemsIndexes.forEach(id => removeIndexes.push(storageIndexes.indexOf(id)));
console.log('storage', storage);
console.log('removed items', items);
removeIndexes.sort().reverse().forEach(index => storage.splice(index,1));
console.log('remaining storage', storage);

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).

mongo/mongoid MapReduce on batch inserted documents

Im creating my batch and inserting it to collection using command i specified below
batch = []
time = 1.day.ago
(1..2000).each{ |i| a = {:name => 'invbatch2k'+i.to_s, :user_id => BSON::ObjectId.from_string('533956cd4d616323cf000000'), :out_id => 'out', :created_at => time, :updated_at => time, :random => '0.5' }; batch.push a; }
Invitation.collection.insert batch
As stated above, every single invitation record has user_id fields value set to '533956cd4d616323cf000000'
after inserting my batch with created_at: 1.day.ago i get:
2.1.1 :102 > Invitation.lte(created_at: 1.week.ago).count
=> 48
2.1.1 :103 > Invitation.lte(created_at: Date.today).count
=> 2048
also:
2.1.1 :104 > Invitation.lte(created_at: 1.week.ago).where(user_id: '533956cd4d616323cf000000').count
=> 14
2.1.1 :105 > Invitation.where(user_id: '533956cd4d616323cf000000').count
=> 2014
Also, I've got a map reduce which counts invitations sent by each unique User (both total and sent to unique out_id)
class Invitation
[...]
def self.get_user_invites_count
map = %q{
function() {
var user_id = this.user_id;
emit(user_id, {user_id : this.user_id, out_id: this.out_id, count: 1, countUnique: 1})
}
}
reduce = %q{
function(key, values) {
var result = {
user_id: key,
count: 0,
countUnique : 0
};
var values_arr = [];
values.forEach(function(value) {
values_arr.push(value.out_id);
result.count += 1
});
var unique = values_arr.filter(function(item, i, ar){ return ar.indexOf(item) === i; });
result.countUnique = unique.length;
return result;
}
}
map_reduce(map,reduce).out(inline: true).to_a.map{|d| d['value']} rescue []
end
end
The issue is:
Invitation.lte(created_at: Date.today.end_of_day).get_user_invites_count
returns
[{"user_id"=>BSON::ObjectId('533956cd4d616323cf000000'), "count"=>49.0, "countUnique"=>2.0} ...]
instead of "count" => 2014, "countUnique" => 6.0 while:
Invitation.lte(created_at: 1.week.ago).get_user_invites_count returns:
[{"user_id"=>BSON::ObjectId('533956cd4d616323cf000000'), "count"=>14.0, "countUnique"=>6.0} ...]
Data provided by query, is accurate before inserting the batch.
I cant wrap my head around whats going on here. Am i missing something?
The part that you seemed to have missed in the documentation seem to be the problem here:
MongoDB can invoke the reduce function more than once for the same key. In this case, the previous output from the reduce function for that key will become one of the input values to the next reduce function invocation for that key.
And also later:
the type of the return object must be identical to the type of the value emitted by the map function to ensure that the following operations is true:
So what you see is your reduce function is returning a signature different to the input it receives from the mapper. This is important since the reducer may not get all of the values for a given key in a single pass. Instead it gets some of them, "reduces" the result and that reduced output may be combined with other values for the key ( possibly also reduced ) in a further pass through the reduce function.
As a result of your fields not matching, subsequent reduce passes do not see those values and do not count towards your totals. So you need to align the signatures of the values:
def self.get_user_invites_count
map = %q{
function() {
var user_id = this.user_id;
emit(user_id, {out_id: this.out_id, count: 1, countUnique: 0})
}
}
reduce = %q{
function(key, values) {
var result = {
out_id: null,
count: 0,
countUnique : 0
};
var values_arr = [];
values.forEach(function(value) {
if (value.out_id != null)
values_arr.push(value.out_id);
result.count += value.count;
result.countUnique += value.countUnique;
});
var unique = values_arr.filter(function(item, i, ar){ return ar.indexOf(item) === i; });
result.countUnique += unique.length;
return result;
}
}
map_reduce(map,reduce).out(inline: true).to_a.map{|d| d['value']} rescue []
end
You also do not need user_id in the values emitted or kept as it is already the "key" value for the mapReduce. The remaining alterations consider that both "count" and "countUnique" can contain an exiting value that needs to be considered, where you were simply resetting the value to 0 on each pass.
Then of course if the "input" has already been through a "reduce" pass, then you do not need the "out_id" values to be filtered for "uniqueness" as you already have the count and that is now included. So any null values are not added to the array of things to count, which is also "added" to the total rather than replacing it.
So the reducer does get called several times. For 20 key values the input will likely not be split, which is why your sample with less input works. For pretty much anything more than that, then the "groups" of the same key values will be split up, which is how mapReduce optimizes for large data processing. As the "reduced" output will be sent back to the reducer again, you need to be mindful that you are considering the values you already sent to output in the previous pass.

Categories

Resources