I have an array of values to delete but want the user to confirm that he wants to delete the value. I plan to use the prompt Node module but don't know how to combine them in such a way that I move to the next value in the array after the user has confirmed (or not) the deletion of the current value. In short, the following code won't work:
var arr = ["1","2","3"];
for (var i in arr) {
console.log("Delete " + arr[i] + "?");
prompt.get(['response'], function (err, result) {
console.log(' reponse: ' + result.response);
// if ...
});
}
Any idea? Many thanks.
#user1280859 is right about the issue but maybe it is better to take adventage of prompt features instead of recursion or asyncronous loop:
var prompt = require('prompt');
prompt.start();
var arr = ["1","2","3"];
var promptArr = arr.map(function(num) {
return "Delete " + num + "?";
});
prompt.get(promptArr, function (err, result) {
console.log(result);
});
OutPut:
prompt: Delete 1?: y
prompt: Delete 2?: n
prompt: Delete 3?: y
{ 'Delete 1?': 'y', 'Delete 2?': 'n', 'Delete 3?': 'y' }
Your code is not working because you're trying to use async calls prompt.get within syncronous loop for.
In case you have to make async calls you should to wait its callbacks. Particulary in your case you can use something like
function removeEl(idx, arr, done){
if(idx>=arr.length) return done(arr);
prompt.get(['response'], function (err, result) {
console.log(' reponse: ' + result.response);
if(/*user said yes*/){
arr.splice(idx, 1);
removeEl(idx, arr, done)
}else{
removeEl(idx+1,arr, done)
}
});
}
function removeWithPrompt(arr, done){
removeEl(0, arr, done);
}
removeWithPrompt([1,2,3], function (modified) {
// modified contains non-deleted elements
})
Or use asyncronous loops like async.each
Related
I have an array from my API that prints like this:
Array [Object, Object, Object, Object, Object]
// if stringified
[{"id":"0","name":"user1","type":"mf","message":"bonjour user1"},
{"id":"1","name":"user2","type":"ff","message":"hello user2"},
{"id":"2","name":"user3","type":"mm","message":"konnichiwa user3"},
{"id":"3","name":"user4","type":"mf","message":"ni hao user4"},
{"id":"4","name":"user5","type":"ff","message":"high 5! user5"}]}
I have an input named content and I would like to see if it matches any of the name in the array; if it does, which id is it; if it's not, the id would be 0.
Eg. if user enters user3, the id would be 2; and if user enters user9, the id would be 0.
I have been struggling to get the value of name from this nested array and below is what I have tried... and the whole code is here. It would be very nice if someone could tell me where have I done wrong:
var data = {};
$.ajax({
url: googleApi,
headers: {
"Authorization": "Basic " + btoa(googleKey + ":" + googleSecret)
},
data: data,
dataType: 'json',
type: 'GET',
success: function(data) {
console.log(data);
console.log(JSON.stringify(data));
function getID(name){
if (name.name == content){
console.log ("matching name" + name.name);
return getID(name.name);
} else {
return name;
}
}
alert(getID(data).id);
return false;
},
error: function(data) {
console.log("ERROR: " + data);
}
});
Updated Answer:
Dont alert the function, just alert the answer inside the loop when it matched.
I typed alexis in the textbox and the output was 1
I have edited my answer.Check it, this is what you have to do in your case:
function getID(name) {
$.each(data,function(key,value){
$.each(value,function(key1,value1){
console.log(value);
if(value1 == content){
alert(value.id);
return;
}
});
});
}
getID(data);
return false;
Take a look at the updated fiddle Fiddle Example
Second Update:
You do not have to use else if condition, simply use a variable to check whether there has been any matches.
You create a empty variable and while looping through the array check whether there is a match and if there is a match, you just feed the variable with that id value and later you will check whether the variable is empty or not and based on that alert(0);
Checkout the Latest Updated Fiddle
As you are using jQuery, you can use the grep function which is intended for searching an array:
var contentArray = [{"id":"0","name":"user1","type":"mf","message":"bonjour user1"},
{"id":"1","name":"user2","type":"ff","message":"hello user2"},
{"id":"2","name":"user3","type":"mm","message":"konnichiwa user3"},
{"id":"3","name":"user4","type":"mf","message":"ni hao user4"},
{"id":"4","name":"user5","type":"ff","message":"high 5! user5"}];
var input = 'user5';
var result = $.grep(contentArray, function(e){ return e.name == input; });
The result is an array with the items found. If you know that the object is always there and that it only occurs once, you can just use result[0].id to get the value. Otherwise you should check the length of the resulting array. Example:
if (result.length === 0) {
// not found
} else if (result.length === 1) {
// access the name property using result[0].id
} else {
// multiple items found
}
So you can design a function for above as per your requirement, something like below:
function getID(myArr, inputName) {
var result = $.grep(myArr, function(e){ return e.name == inputName; });
if (result.length === 0) {
return 0;
} else if (result.length === 1) {
return result[0].id
} else {
// multiple items found
// May be it's not true in your case
return result[0].id
}
}
I have an array of image urls to download asynchronously. I need to wait the next iteration until the first async task completed. Here is my code snippet:
downloadQueue.forEach(function (download, idex) {
download.startAsync().then(
function (res) { console.log('onSuccess'); },
function (err) { console.log('onError'); },
function (msg) {
var progressPrecent = parseFloat(msg.bytesReceived / msg.totalBytesToReceive * 100).toFixed(2);
console.log('Progress: ' + progressPrecent);
});
});
After download completion of the first url, next one(iteration) should be started. How should i modified this code to get work on that? Any help..
You will want to do something recursive.
That way you only start the next download after the promise returns from the download.
//Declare the recursive function
var startDownload = function(downloadQueue,i) {
download.startAsync().then(
function (res) { console.log('onSuccess'); },
function (err) { console.log('onError'); },
function (msg) {
var progressPrecent = parseFloat(msg.bytesReceived / msg.totalBytesToReceive * 100).toFixed(2);
console.log('Progress: ' + progressPrecent);
//If there are more items to download call startDownload
if(i < downloadQueue.length) {
startDownload(download,i+1);
}
});
}
//Initialize Function
startDownload(downloadQueue,0);
I calling getBubblesUserAccess that returns json objects that are orderd in a special way. This results i wanna run a foreach and get other messages but there i wanna return them in "order". I know that it will run these async but there must be a way that i can force it to "sequential" execution. (above code is my last attempt to add a defer...)
Example
pseudo code - get my groups
{
"id":"016cd1fc-89a3-4e4a-9e6e-a102df1b03d9",
"parent":"53750396-7d26-41f3-913d-1b93276b9e09",
"name":"XX",
"createdBy":"c9c63080-2c5b-4e8e-a093-2cfcd628a9d0",
"hasWriteAccess":true,
"hasCreateAccess":false,
"hasDeleteAccess":false,
"hasAdminAccess":false,
"settingsBubbleId":"00000000-0000-0000-0000-000000000000"
},
{
"id":"016cd1fc-89a3-4e4a-9e6e-a102df1b03d9",
"parent":"53750396-7d26-41f3-913d-1b93276b9e09",
"name":"XX",
"createdBy":"c9c63080-2c5b-4e8e-a093-2cfcd628a9d0",
"hasWriteAccess":true,
"hasCreateAccess":false,
"hasDeleteAccess":false,
"hasAdminAccess":false,
"settingsBubbleId":"00000000-0000-0000-0000-000000000000"
}
From this result i wanna iterate over those parent id strings and call another service that respond with this.
pseudo code
for each group above call another service with parent id and get result. This result will be added to a new JSON object.
"messages":[
{
"id":"f1d1aeda-d4e2-4563-85d5-d954c335b31c",
"text":"asd",
"sent":"2015-09-10T22:31:09.897+00:00",
"sender":"6b9e404b-ef37-4d07-9267-3e7b2579003b",
"senderName":"XXX XXXX"
},
{
"id":"a7ac0432-e945-440e-91ce-185170cbf3de",
"text":"asd",
"sent":"2015-09-10T22:28:24.383+00:00",
"sender":"c9c63080-2c5b-4e8e-a093-2cfcd628a9d0",
"senderName":"ZZZZZ ZZZZ"
},
My problem is that my second foreach are running async (as it should) and i want it to resolve back in same order as first json object...
My code::
var loadBubblesAccess = function () {
if (vm.running && angular.isDefined(vm.running)) { return; }
vm.running = true;
vm.bubblesWithMessages = null;
return BubbleFactory.getBubblesUserAccess().then(function (bubblesAccessTo) {
return bubblesAccessTo;
});
},
loadSubBubbles = function (bubblesAccessTo) {
/**
* Result from chain method with all bubbles user has access to.
*/
var promiseArray = [];
//var promiseArrayError = [];
var i = 0;
/**
* Creates a defer object so that we will not resolve before for each loop has been gone thru.. async problems.
*/
var deferred = $q.defer();
angular.forEach(bubblesAccessTo, function (bubble) {
$log.error(JSON.stringify(bubblesAccessTo));
/**
* Get 20 because default thats default and cache and e-tags are done to that number..
*/
BubbleFactory.getBubbleMessages(bubble.id, 0, 20, false).then(function (data) {
i++;
if (data.messages.length > 0) {
promiseArray.push({ bubbleSortOrder: i, bubbleId: bubble.parent, bubbleName: bubble.name, bubbleMessagesId: bubble.id, bubbleMessages: smartTrim(data.messages[0].text, 400, ' ', ' ...'), bubbleMessagesSent: data.messages[0].sent });
}
else {
// console.log("YYYY::: " + bubble.parent);
promiseArray.push({ bubbleSortOrder:i, bubbleId: bubble.parent, bubbleName: bubble.name, bubbleMessagesId: bubble.id, bubbleMessages: 'Inget meddelande än..', bubbleMessagesSent: '' });
}
});
/**
* Check if we have gone thru all bubbles - when finished we resolve defer object.
*/
if(i===bubblesAccessTo.length)
{
deferred.resolve(promiseArray);
}
});
//$log.debug.log(promiseArray);
vm.bubblesWithMessages = promiseArray;
promiseArray.length = 0;
vm.running = false;
};
loadBubblesAccess().then(loadSubBubbles);
The $q service in AngularJS is described as "lightweight" because it only implements the functions 90% of people need. That keeps its code size small - at the expense of not being able to address requests like yours very easily.
If you have the option, try an alternative such as bluebird. Bluebird provides a reduce() function that can execute an array of promises serially, and return their results in the order they were requested. It makes this task straightforward because your result array will match your data array and you can match up the results very easily.
If you do NOT have that option, there is a standard (if not-exactly-simple) technique with promises where you build an array of the elements you want to promise, then call the processing function (that returns a Promise) on the first value (popped from the array). In the .finally() handler, call the processing function recursively with the next value until it is empty (or an error occurs).
Pseudo-code for this:
var valuesToProcess = [1, 2, 3],
results = [];
function processValue(val) {
myProcessingFunction(val).then(function(result) {
results.push(result);
}).catch(function(e) {
console.log('FAIL!', e);
}).finally(function() {
if (valuesToProcess.length > 0) {
processValue(valuesToProcess.shift());
} else {
// All done - do something with results here
}
});
}
// Note: No error checking done, assumes we have work to do...
processValue(valuesToProcess.shift());
You'll need to adapt this to your use-case but it's a simple technique that guarantees serial operation and result-handling.
I have a the following code that currently works
but...
I know it is not elegant and can be done much more efficiently.
What I am trying to do is take an array of emails, search if they exist in a corresponding db with a particular templateName, and for those that do not exist (i.e are 'new'), list them back on the returned page to the user. However, they end up waiting quite a while if there are a lot of emails to check.
This is the first time I'm using async and it may not actually be the best way to do this. Some of the below has been modified from what I am actually using currently to make it easier to read/follow.
Basically, from my handler, I call the following (where both emailArray and templateName are extracted from the request parameter passed in).
var newEmails = "";
async.eachSeries(emailArray, function(entry, cb) { // check each item in array (these are the potential new emails)
utils.emailAddressAndTemplateExists(entry.toString().toLowerCase(), templateName, function (err, emailExists, templateExists ) {
if (emailExists) {
if (templateExists) {
++existingCount;
} else if (emailExists && !templateExists) {
} else {
console.log('template does not exist');
}
} else {
++newCount;
newEmails = newEmails + entry + "</br>";
}
cb();
});
//cb();
}, function (err) {
if (err) { throw err; }
var content = utils.getHTMLHead() + newEmails + utils.getHTMLClose();
utils.writeHTMLPage(response, content);
});
The utils call does the following: (the writeHTMLPage simply adds the required html tags and sends back to response).
//checks for a single email address
var emailAddressExists = function(emailAddress, callback) {
if (emailAddressCollection == null) {
//console.log("it was null " + db_singleton + " " + dbName);
emailAddressCollection = db_singleton.collection(dbName);
}
emailAddressCollection.find( { "emailAddress" : emailAddress.toLowerCase() } ).toArray( function (err, docs) {
if (err) { console.err(err); }
if (docs.length == 0) {
callback(null, false, docs.EmailsSent);
} else {
doc = docs[0];
callback(null, true, doc.EmailsSent);
}
});
}
// check for email And template
var emailAddressAndTemplateExists = function (emailAddress, templateName, callback) {
emailAddressExists(emailAddress, function (err, returnVal, templates) {
if (returnVal) {
if (templates != null) {
callback (null, true, templates.hasOwnProperty(templateName)) // email exists, checking for templateName
} else {
callback (null, true, false); // email exists, no templates at all exist
}
} else {
callback (null, false, false); // email does not exist, templates must be false
}
});
}
//creates HTML formated respnse data
function writeHTMLPage(response, content) {
// if (err) { console.error(err); response.send("Error " + err); }
response.writeHead(200, {"Content-Type": "text/html"});
response.write(content);
response.end();
}
What are more elegant and efficient way to do this?
This looks like it's constructed according to how you'd normally see it. You can look into Promises with ES6 to get a better program flow:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Basically, it lets you chain together functions. It's not too bad for what you're doing now, it might help out some, but if you have 4-5 callbacks nested together, that's when promises can be very helpful.
You'll just have to work through structuring your code differently to use Promises, but it'll make "callback hell" less of an issue.
I have the below that runs a few functions to grab different data and i am trying to create/append to an array to "create" a new array that i can then JSON.stringify and pass to the Jade view, i seem to only be able to get the "k" variable of the two loops it does and outputs as "coin1coin2" with the "" instead of a JSON array with the balance and addresses (multiples) after each coin name.
// list all wallets //
app.get('/wallet', function(req, res){
var coins = app.get('coins');
var addresses = "";
for (var k in coins) {
addresses += k;
console.log('Coin: ' + k);
//console.log(k+' => '+coins[k]);
connect(coins[k]);
client.getBalance('44abc0a80102', 6, function(err, balance) {
if (err) return console.log(err);
addresses += balance;
//console.log('Balance: ' + balance);
});
client.getAddressesByAccount('44abc0a80102', function(err, address) {
if (err) return console.log(err);
addresses["address"] += address;
});
}
console.log(addresses);
res.render('wallet', {
title: 'Wallet',
address: JSON.stringify(addresses)
});
});
In the Jade view i am just doing p= address to show the full array so i can at least see what i am getting before creating logic in the view to loop through it.
The problem here is that your for loop is synchronous, whereas your client.getBalance and client.getAddressesByAccount calls are (almost definitely) not.
When your for loop finishes, your client.getBalance and client.getAddressesByAccount operations are still in progress, so they haven't had a chance to populate your addresses thing yet. Thus it is empty when it's passed to the templating system.
You probably want to use something like async's map method to do this instead. It'd look something like this:
// list all wallets
app.get('/wallet', function(req, res){
// array? object?
var coins = app.get('coins');
async.map(coins, function(coinId, cb) {
// ???
console.log('Coin: ' + JSON.stringify(coin));
// not sure what this does!
connect(coin);
client.getBalance('44abc0a80102', 6, function(err, balance) {
if (err) return cb(err);
client.getAddressesByAccount('44abc0a80102', function(err, address) {
if (err) return cb(err);
return cb(null, {
address: address,
balance: balance,
});
});
});
}, function(err, addresses) {
// note that we only act on an error here
if (err) {
return console.log(err);
}
// [{address: ..., balance: ...}, ...]
console.log(addresses);
res.render('wallet', {
title: 'Wallet',
address: JSON.stringify(addresses),
});
});
});
The addresses value will be an array of {address: ..., balance: ...} objects. I'm not sure what you were expecting it to be, since at one point you treat it like a string, then you treat it like an object.