Looping through a series of GET requests - javascript

I have a get request that looks for a specific ID within a tree, and then pulls back the value from that ID. I need to loop through a series of these get requests, each with a similar ID (each ID increases in value by one).
I have created a standard loop using hard coded values but I'm struggling to set the variable based on dynamic values coming out of the tree.
For example, I can set a variable like this:
var cars = [entry.get('sub_menu.sub_menu_link.0.title'), entry.get('sub_menu.sub_menu_link.1.title'), entry.get('sub_menu.sub_menu_link.2.title')];
This grabs all the values from these areas of the tree.
But I don't know how many of these there will be so I can't hard code it in this way. I need to be able to replace 0, 1 and 2 in those values with a loop that adds a new get request and increases the integer between "link." and ".title" each time.
Expected result would be to add as many get requests in to the variable as it finds, with the integer increased for each request, until it finds no more.
Full example code with hard coded get requests is below (which won't actually work because the tree isn't being pulled in. For example purposes only):
Query.fetch()
.then(function success(entry) {
var subMenu = [entry.get('sub_menu.sub_menu_link.0.title'), entry.get('sub_menu.sub_menu_link.1.title'), entry.get('sub_menu.sub_menu_link.2.title')];
var text = "";
var i;
for (i = 0; i < subMenu.length; i++) {
text += subMenu[i] + "<br>";
}
document.getElementById("subMenu-container").innerHTML = text;
},
function error(err) {
// err object
});

I'm answering based on three assumptions:
entry.get is synchronous
entry.get returns null or undefined if it couldn't get the string matching the argument we pass it
Although your code is using promises, you want to keep it to ES5-level language features (not ES2015+)
See inline comments:
Query.fetch()
.then(function success(entry) {
// Loop getting titles until we run out of them, put them in `titles`
var titles = [];
var title;
while (true) { // Loop until `break;`
// Use string concat to include the value of `titles.length` (0, 1, ...) in the string
title = entry.get('sub_menu.sub_menu_link.' + titles.length + '.title');
if (title === null || title === undefined) {
break;
}
titles.push(title);
}
// Use `Array.prototype.join` to join the titles together with <br> in-between
document.getElementById("subMenu-container").innerHTML = titles.join("<br>");
}, function error(err) {
// Handle/report error
});

Related

How to perform fast search on JSON file?

I have a json file that contains many objects and options.
Each of these kinds:
{"item": "name", "itemId": 78, "data": "Some data", ..., "option": number or string}
There are about 10,000 objects in the file.
And when part of item value("ame", "nam", "na", etc) entered , it should display all the objects and their options that match this part.
RegExp is the only thing that comes to my mind, but at 200mb+ file it starts searching for a long time(2 seconds+)
That's how I'm getting the object right now:
let reg = new RegExp(enteredName, 'gi'), //enteredName for example "nam"
data = await fetch("myFile.json"),
jsonData = await data.json();
let results = jsonData.filter(jsonObj => {
let item = jsonObj.item,
itemId = String(jsonObj.itemId);
return reg.test(item) || reg.test(itemId);
});
But that option is too slow for me.
What method is faster to perform such search using js?
Looking up items by item number should be easy enough by creating a hash table, which others have already suggested. The big problem here is searching for items by name. You could burn a ton of RAM by creating a tree, but I'm going to go out on a limb and guess that you're not necessarily looking for raw lookup speed. Instead, I'm assuming that you just want something that'll update a list on-the-fly as you type, without actually interrupting your typing, is that correct?
To that end, what you need is a search function that won't lock-up the main thread, allowing the DOM to be updated between returned results. Interval timers are one way to tackle this, as they can be set up to iterate through large, time-consuming volumes of data while allowing for other functions (such as DOM updates) to be executed between each iteration.
I've created a Fiddle that does just that:
// Create a big array containing items with names generated randomly for testing purposes
let jsonData = [];
for (i = 0; i < 10000; i++) {
var itemName = '';
jsonData.push({ item: Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) });
}
// Now on to the actual search part
let returnLimit = 1000; // Maximum number of results to return
let intervalItr = null; // A handle used for iterating through the array with an interval timer
function nameInput (e) {
document.getElementById('output').innerHTML = '';
if (intervalItr) clearInterval(intervalItr); // If we were iterating through a previous search, stop it.
if (e.value.length > 0) search(e.value);
}
let reg, idx
function search (enteredName) {
reg = new RegExp(enteredName, 'i');
idx = 0;
// Kick off the search by creating an interval that'll call searchNext() with a 0ms delay.
// This will prevent the search function from locking the main thread while it's working,
// allowing the DOM to be updated as you type
intervalItr = setInterval(searchNext, 0);
}
function searchNext() {
if (idx >= jsonData.length || idx > returnLimit) {
clearInterval(intervalItr);
return;
}
let item = jsonData[idx].item;
if (reg.test(item)) document.getElementById('output').innerHTML += '<br>' + item;
idx++;
}
https://jsfiddle.net/FlimFlamboyant/we4r36tp/26/
Note that this could also be handled with a WebWorker, but I'm not sure it's strictly necessary.
Additionally, this could be further optimized by utilizing a secondary array that is filled as the search takes place. When you enter an additional character and a new search is started, the new search could begin with this secondary array, switching to the original if it runs out of data.

How to pass a function only after an entire array has been read and not for each element?

I'm trying to run a function when reading an array, but instead of running the function to each element, which I'm currently using forEach for, I want to make the script to read the entire array and then pass a function.
What I'm trying to say is this:
data.forEach(movie => {
// Log each movie's title
//console.log(movie.title);
// Check if the userWord matches
if (movie.title.toUpperCase().includes(userWord.toUpperCase())) {
alert("YES");
} else {
alert("NO").
}
});
Let's say my array is: array = ["Aa", "Ba", "Ca", "Da"];
If the user enters: a, then the script would alert("YES") four times, and I want to make it alert just once, at the end of the iteration.
For the same example, if the users enters: B, then the script would first alert("NO") once, then alert("YES"), then alert("YES") 2 times, and I want to make it just alert("YES")once, in the end.
Finally, if the users enters: Ferrari, then the script would alert("NO") four times, and I just want it to alert("NO") at the end.
I tried to make it very clear here, that's why the three "cases" of what is happening.
In the end, I want to know if there is a method that is kinda the opposite of the forEach or the common for. Something that just executes the function after reading the entire array.
Change the alert to a bool variable
Remove else (it would only overwrite any match)
if bool statement outside the loop to perform actions
if you want a list of the results, you should store the names in an array and outside of the loop - print.
see below:
Non-loop method:
data = ["test","hello", "hello1"];
search = "lo";
const matches = data.filter(movie => movie.includes(search));
alert(matches) //array of only matches - only has hello and hello 1
I don't know if there are performance gains against a loop... I suppose you could do a side by side comparison on a large dataset
Loop method:
var matches = "";
data.forEach(movie => {
// Check if the userWord matches
if (movie.title.toUpperCase().includes(userWord.toUpperCase())) {
matches += movie.title + "<br> ";
}
});
if (matches.length > 0)
{
document.getElementById("results").innerHTML = matches;
} else {
alert("No match found");
}
You'll see more customization on the first loop, but I think filtering data first is the way to go.
I think closest what you can get is some. Here is example
let data = ["test","hello", "hello1"];
let userWord = "el";
let res = data.some(movie => movie.toUpperCase().includes(userWord.toUpperCase()));
console.log(res) // prints true- means there was at least one "match", so you can alert
You could use the filter array function to filter array elements that match your criteria and then finally alert only if a match is found. Using this method you could get all the elements that have matched to your userWord. That is, the match array will contain all the possible matches.
var data = ['Aa', 'Bb', 'Cc', 'Dd'];
var flag = false;
var userWord = 'F'
var match = data.filter(movie => movie.indexOf(userWord) !== -1);
if(match.length)
console.log('YES');
else
console.log('NO');
If I understand the question correctly, I believe you want to execute something if a predicate matches at least one of the items in the array (correct me if I'm wrong). For that you can use the some method like so:
if (movies.some((movie) => movie.toUpperCase().includes(userWord.toUpperCase()))) {
alert('YES');
} else {
alert('NO');
}

using javascript with a string that contains

Hi I am currently using java to hide certain tabs and fields on my forms depending on the population of dropdowns, for example here is a code that is working:
//Display Transfer tab if it is a transfer application
var ctrlApplicationType = Runner.getControl(pageid, 'ApplicationType');
ctrlApplicationType.on('change', function(e)
{
if (this.getValue() == 2)
{
var tabs = pageObj.getTabs(); tabs.show(2);
}
else
{
var tabs = pageObj.getTabs(); tabs.hide(2);
}
}
);
In the example above the dropdown is fed from a lookup table and returns the primary key INT, hence ==2 works fine.
However I now have a problem when I am trying to get this to work with a checkbox, because the problem is a checkbox can have multiple options.
My lookup table for checkbox has 5 options, so if i ticked option 1, 2 and 3, the field (string) is stored as 1,2,3.
What I need to do is to do change the above code so it returns true if it contains 1, ie
if (1,2,3) contains 1 then true
if (2,3) contains 1 then false.
Any ideas would be much appreciated
Okay, against my better judgement (I'd really like to see you make your own attempt based on the information I've already given you), here you go...
var selectedString = "1,2,3"; // from your code, this is this.getValue()
var selectedArray = selectedString.split(","); // split the string into an array using a comma (,) as the split point
var foundInArray = selectedArray.includes('1'); // foundInArray is now a boolean indicating whether or not the value '1' is one of the values in the array.
if(foundInArray)
{
// do the found action
}
else
{
// do the not found action
}
If you want to compare against integer values instead of string values, that's easy enough too.
var integerArray = selectedArray.map(function(x){ return parseInt(x); });
var foundInArray = integerArray.includes(1);
Finally, all of this can be chained into a one-liner:
if(selectedString.split(",").map(function(x){return parseInt(x);}).includes(1))
{
// do found action
}
else
{
// do not found action
}
To iterate through a fixed list and show/hide each, you can do this...
var possibleTabs = [1,2,3,4,5];
for(n in possibleTabs)
{
if(selectedString.split(",").map(function(x){return parseInt(x);}).includes(n))
{
var tabs = pageObj.getTabs(); tabs.show(n);
}
else
{
var tabs = pageObj.getTabs(); tabs.hide(n);
}
}
This, of course, assumes that there is a relation between the checkbox value and the tabs. If there's not, then you're going to have to list them all out as individual if/elseif/else statements, and that is going to get out of hand really quickly.

Generating set of random elements with further generation of 'one by one'

I am generating elements that have random data attributes like so :
generateCards : function(n)
{
var actions = ['press', 'blue-right', 'blue-left', 'red-right', 'red-left'],
i = n,
ran,
actions_cpy = actions.slice();
for (; i--;) {
ran = (Math.random() * actions_cpy.length)|0;
$('#container-game-mobile').prepend(
$('<div>', {
// remove and return a random string from the array
'class': 'game-card-mobile',
'data-action': actions_cpy.splice(ran, 1)[0]
})
);
// load the array backup with values when it is empty
if (actions_cpy.length === 0) {
actions_cpy = actions.slice();
}
}
}
Function works in a way where there are more or less equal amounts of each data atribute from actions array. I initially generate 10 elements so n = 10 due to the nature of application every time an action is performed on a .game-card-mobile I destroy it and need to generate new one so, call above function but now with n = 1 . Issue here is that, I somehow need to call elements that will still differ, so prevent 'blue-right' appearing over and over again.
It's a bit confusing, but I think I get the problem.
You need a function to get your actions, that function will return a random of this array, to this function, pass a param of your last actions randomised and if your random is the same as your last random, call the same function again, it will prevent your problem.

delete specific xml node Javascript

My xml file is like:
it contains different 'object' nodes and in different objects there are different parameters one is deleted parameter.
I want to delete the all 'object' nodes that contains the deleted parameter 1.
This is the code that deletes the node object which has a parameter node deleted =1:
x=xmlDoc.documentElement;
for(var count=0; count<5;count++){
var y=x.getElementsByTagName("deleted")[count]; //Find that nodes arent
if(y.textContent == "1") {
var z=y.parentNode; //delete the node from the parent.
x.removeChild(z);
Xml2String1= new XMLSerializer().serializeToString(x);
}
}
Your loop is incorrect:
for(var x1=0; x1<5;x1++){
var y=x.getElementsByTagName("deleted")[x1];
Your loop runs for 5 iterations without regard for the number of <deleted> elements are found. Each time through the loop you search again and get a new NodeList/HTMLCollection of the remaining <deleted> elements, but your loop counter is incremented regardless.
Try this instead:
var deletedNodesList = x.getElementsByTagName("deleted");
var nodesToDelete = [];
for (var index = 0; index < deletedNodes.length ; index += 1)
{
var node = deletedNodes[index];
if (node.textContent == "1")
{
nodesToDelete.push( node.parentNode ); //delete the node from the parent
}
}
nodesToDelete.forEach( function() { x.removeChild(this); } );
Note that, per the documentation on MDN, the NodeList is a live collection, so don't modify it while you are processing it.
PS.
I second raam86's recommendation to use sane (meaningful) variable names. Meaningful variable names make it easier to understand the code, which makes it easier to write correct code and to resolve problems in incorrect code.

Categories

Resources