I have lots of packets that come through to my javascript via web sockets, each of these packets have a packet id, I want to create some sort of array or object that assigns each packet id a function to call, which would handle that packet.
I also want to include the 'packetData' parameter to all functions that are called, so it can get the other parts of data inside the function in order to handle the packet appropriately.
Heres what I have so far...
I listen for a message on WebSockets...
socket.onmessage = function (e) {
onPacket(JSON.parse(e.data));
};
Then I pass it to this function, I send my data in JSON format so I parse it to a key value object, then pass it to this function...
function onPacket(packetData) {
const packetId = packetData['packet_id'];
// TODO: Call the function...
}
The part I am stuck with is how do I create the array or keyvalue object to call the function? I store all my functions in a seperate file called "packet_events.js"
I did have a go at doing it myself (the object key value) but I was told it isn't valid JS, I'm stuck and sort of need some help, can anyone help?
My attempt:
var incomingHeaders = {
1: packetOne(),
};
If I still haven't made it clear, I want something like this, but with an array to save me constantly writing if statements..
function onPacket(packetData) {
const packetId = packetData['packet_id'];
if (packetId == 1) {
packetOne(packetData);
}
if (packetId == 2) {
packetTwo(packetData);
}
if (packetId == 3) {
packetThree(packetData);
}
}
Use object instead of arrays and create maping of ids into functions e.g.
var idToFunction = {
0: packetOne,
1: packetTwo,
2: packetThree
}
then inside onPacket your retrievied function based on id and executes it
function onPacket(packetData) {
const packetId = packetData['packet_id'];
var processPacket = idToFunction[packetId];
if (processPacket) {
processPacket(packetData);
}
else {
throw new Error("Invalid packed id: "+packetId);
}
}
Step 1: Create a function object that holds mapping from your packedId to your function.
Step 2: Iterate over the packets array to call the respective function.
Follow the snippet:
function packetOne() {
console.log('one')
}
function packetTwo() {
console.log('two')
}
let functionObject = {
1: packetOne,
2: packetTwo,
}
let packets = [
{ packetId: 1},
{ packetId: 2}
]
packets.forEach(function(packet){
functionObject[packet.packetId]()
})
Related
Im trying to get data from an API that send json I succeeded in retrieving the data and all pushing it into 1 array but the problem is that when I sort the array and then console.log it its still random for some reason or sometimes it didn't even pull all the data from the API so I'm not getting all the data in the log when it sorts. (that's still random)
script.js
"use strict";
const dataLinks = [
"https://api.data.amsterdam.nl/bbga/cijfers/",
"https://api.data.amsterdam.nl/bbga/gebieden/",
"https://api.data.amsterdam.nl/bbga/groepen/",
"https://api.data.amsterdam.nl/bbga/meta/",
"https://api.data.amsterdam.nl/bbga/themas/",
"https://api.data.amsterdam.nl/bbga/variabelen/"
];
const allDataInArray = [];
let ResetNumber = 0;
init();
//Go thruw all links
function init() {
for (ResetNumber = 0; ResetNumber < dataLinks.length; ResetNumber++) {
GetAllData(dataLinks[ResetNumber]);
}
makeArrays();
}
//Open the links in a get GET request
//then make from the json an Array
//push all into 1 array
function GetAllData(hrefLink) {
let xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
let myObj = JSON.parse(this.responseText);
// console.log(myObj)
allDataInArray.push(myObj);
}
};
xmlhttp.open("GET", hrefLink, true);
xmlhttp.send();
}
function makeArrays() {
allDataInArray.sort();
console.log(allDataInArray);
}
By default, the sort() method sorts the values as strings in alphabetical and ascending order.
if you want to sort array for an object or number then use it with compare function
array.sort(compareFunction)
You have two problems here.
The first one ist that your return values are Objects and you will have to define a custom sort function to pass to Array.sort in order to get a correctly sorted result.
Another problem is that you have to wait for all requests to finish before sorting the result array. A standard way to do that is to use Promise.all and fetch (which wraps a request into a Promise).
You can use the following code as a template and fill in a definition of sortFunction to make it work altogether.
const dataLinks = [
"https://api.data.amsterdam.nl/bbga/cijfers/",
"https://api.data.amsterdam.nl/bbga/gebieden/",
"https://api.data.amsterdam.nl/bbga/groepen/",
"https://api.data.amsterdam.nl/bbga/meta/",
"https://api.data.amsterdam.nl/bbga/themas/",
"https://api.data.amsterdam.nl/bbga/variabelen/"
];
let allDataInArray;
Promise.all(dataLinks.map(link => fetch(link).then(response => { if (response.ok) { return response.json(); }})))
.then(result => { allDataInArray = result.sort(sortFunction); })
Note that any code which wants to use allDataInArray has to wait as well. You can implement the observer pattern for that.
So here's the problem. I have a REST API that handles a booking creation, however, before saving the booking inside mongo it validates if there is a clash with another booking.
exports.create = function(req, res) {
var new_type = new Model(req.body);
var newBooking = new_type._doc;
//check if the new booking clashes with existing bookings
validateBooking.bookingClash(newBooking, function(clash){
if(clash == null) // no clashes, therefore save new booking
{
new_type.save(function(err, type) {
if (err)
{
res.send(err); //error saving
}
else{
res.json(type); //return saved new booking
}
});
}
else //clash with booking
{
//respond with "clashDate"
}
});
};
Here you have the validation function to check if there is a clash with bookings on the same day:
exports.bookingClash = function (booking, clash) {
//find the bookings for the same court on the same day
var courtId = (booking.courtId).toString();
Model.find({courtId: courtId, date: booking.date}, function(err, bookings) {
if(err == null && bookings == null)
{
//no bookings found so no clashes
clash(null);
}
else //bookings found
{
//for each booking found, check if the booking start hour falls between other booking hours
for(var i = 0; i<bookings.length ; i++)
{
//here is where I check if the new booking clashes with bookings that are already in the DB
{
//the new booking clashes
//return booking date of the clash
clash(clashDate); //return the clashDate in order to tell the front-end
return;
}
}
//if no clashes with bookings, return null
clash(null);
}
});
};
So, ALL of this works with one single new booking. However, now I want to be able to handle a recursive booking (booking that is made weekly). I have recreated the "create" function and call the validateBooking.bookingClash function inside a for loop.
Unfortunately, when I run this, it calls the bookingClash function perfectly, but when it reaches the line making the search in the database:
Model.find({courtId: courtId, date: booking.date}, function(err, bookings)
It does not wait for the callback and before handling the response "clash", makes i++ and continues.
How can I make it work and wait for the callback?
var array = req.body;
var clashes = [];
for(var i = 0; i<array.length;i++)
{
validateBooking.bookingClash(array[i], function(clash)
{
if(clash)
{
clashes.push(clash);
}
else{
console.log("no clash");
}
}
}
Seems like a basic async call problem, for loops do not wait for callbacks to be called.
You could use async 'series' function for exmaple instead of the for loop. This way each find will get called after the previous one.
Mongoose also has a promise based syntax which can help you : http://mongoosejs.com/docs/promises.html
You Can use async eachSeries
async.eachSeries(users, function iterator(user, callback) {
if(something) {
//thing you want to do
callback();
} else {
callback();
}
}
Since you are using callback functions there are two ways you could try to solve this:
1) use some external library that allows you to perform an asynchronous map operation and run all the checks for each clash. Once they are done check the combined results for a clash and proceed accordingly
I would suggest using the async library
your code would look something like:
async.map(array,(entry,callback) => validateBooking.bookingClash(entry,callback),(error,mappingResults)=>{...})
2) you could try to change this function to a recursive one
`function recursiveValidation(arrayToCheck,mainCallback){
if(arrayToCheck.length === 0) {
return cb(null} // end of array without errors
}
validateBooking.bookingClash(_.head(arrayToCheck), function(clash)
{
if(clash)
{
return mainCallback(clash);
}
return recursiveValidation(_.tail(arrayToCheck),mainCallback);
}
}`
The above code is just a mockup but it should show the point.
The _ is lodash
No need to changing anything in your code except the declaration use let instead of var and your loop should work.
var array = req.body;
var clashes = [];
`
for(**let** i = 0; i<array.length;i++)
{
validateBooking.bookingClash(array[i], function(clash)
{
if(clash)
{
clashes.push(clash);
}
else{
console.log("no clash");
}
}
}`
You have to understand the difference between let and var. Also why var cannot be used for running async code inside a loop.
Learn about let: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
I found the way to get this done after trying all of your answers.
What I had to do was this:
validateBooking.singleBooking(new_type._doc, newBookingClubId, function (clash) {
if (clash == null) // no clash
{
validatorArray.push(0);
if(validatorArray.length == array.length) //has received everything from mongo
{
console.log("Clashes: " + clashes.toString());
if(validatorArray.indexOf(1) > -1) //contains a clash
{
var error = {
code: 409,
message: "409 Conflict",
clashes: clashes
};
errorsHandler.handleError(error, res);
}
This way, I created an array called "validatorArray" that was called every time I received something back from Mongo.
This way I could easily compare the length of the array of bookings and the validatorArray length. When they were equal, it meant that it had received everything back from mongo and could send back the response.
Thanks for the help!
I have data coming from the db and I want the user to be able to add a new array after adding the new changes. I want a function that can be used multiple times for different ajax calls.
My object array is var basinSub and the content of the object array is:
{BasinId: (array stuff), SubBasinId: (array stuff), SubBasinName: (array stuff)}
How can I create a for key in data function that I can add a new array to the basinSub object?
I was thinking something like this but it's not working:
function locationChangeData(value, desc) {
for(var i in basinSub) {
if(basinSub[i].value == value) {
basinSub[i].desc = desc;
}
}
}
Then I would call the function in the ajax success function:
locationChangeData(basinSub, newSubBasinToAddSub);
Which newSubBasinToAddSub is the new array to add to basinSub.
This might do it
//function to add new changes
function targetChange(target, source) {
Object.keys(source).forEach(function(key) {
for(i=0;i<source.length;i++) {
target[key][target[key].length++] = source[key][i];
}
});
}
I have some code that exercises the “invalid values” setting on an element range index. In this case, I have configured a dateTime element range index on the onDate element in my database (which will apply to both XML elements and JSON properties). I’ve set that index to reject invalid values. This setting means if I try to set the value of an onDate element and it is not castable to a dateTime or is null (literal null in JSON or xsi:nil="true" in XML), my update will fail. (The opposite behavior is to completely ignore invalid values.)
I tried the following code in Server-Side JavaScript in MarkLogic 8.0-4:
'use strict';
declareUpdate();
var errors = [];
var inputs = {
'/37107-valid.json': (new Date()).toISOString(),
'/37107-invalid.json': 'asdf', // Should throw an error
'/37107-null.json': null
};
for(var uri in inputs) {
try {
xdmp.documentInsert(
uri,
{ 'onDate': inputs[uri] },
xdmp.defaultPermissions(),
['37107'] // Collections
);
} catch(err) {
errors.push(err);
}
}
errors.length;
I would have expected my request to succeed and to end up with 1 === errors.length, because only the second insert would have failed because 'asdf' is not castable as a dateTime and it is not null. However, instead I get an XDMP-RANGEINDEX error and my transaction fails. Why doesn’t my try/catch work here?
The issue is how MarkLogic processes update transactions. Rather than actually changing the data with each xdmp.docuentInsert(…) call, MarkLogic queues up all of the updates and applies them atomically at the end of the request. (This is also why you can’t see database updates within the same transaction.) Thus, the error isn’t being thrown until after the loop has executed and the database tries to commit the queued transactions. This behavior is the same in XQuery (slightly simplified):
let $uris := (
'/37107-valid.xml',
'/37107-invalid.xml',
'/37107-null.xml'
)
let $docs := (
<onDate>{fn:current-dateTime()}</onDate>,
<onDate>asdf</onDate>,
<onDate xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
)
return
for $uri at $i in $uris
return
try {
xdmp:document-insert($uri, $docs[$i], (), ('37107'))
} catch($err) {
xdmp:log($err)
}
In order to catch the errors synchronously, you’d need to put each update into its own transaction. In general, this approach will be much slower and resource intensive than MarkLogic’s default transaction handling. However, it’s illustrative here to demonstrate what’s happening under the covers and can come in handy for specific use cases, like this one.
In the example below, I use xdmp.invokeFunction() to “call” a function in a separate transaction from the parent request. (First-class functions for the win!) This allows the updates to be fully applied (or rolled back with an error) and the calling module to see the updates (or errors). I’ve wrapped the low-level xdmp.invokeFunction() in my own applyAs() function to provide some niceties, like correctly passing function arguments to the curried function.
'use strict';
var errors = [];
var inputs = {
'/37107-valid.json': (new Date()).toISOString(),
'/37107-invalid.json': 'asdf',
'/37107-null.json': null
};
var insert = applyAs(
function(uri, value) {
return xdmp.documentInsert(
uri,
{ 'onDate': inputs[uri] },
xdmp.defaultPermissions(),
['37107']
);
},
{ isolation: 'different-transaction', transactionMode: 'update' },
'one'
);
for(var uri in inputs) {
try {
insert(uri, inputs[uri]);
} catch(err) {
errors.push(err);
}
}
errors.length; // Correctly returns 1
// <https://gist.github.com/jmakeig/0a331823ad9a458167f6>
function applyAs(fct, options, returnType /* 'many', 'one', 'iterable' (default) */) {
options = options || {};
return function() {
var params = Array.prototype.slice.call(arguments);
// Curry the function to include the params by closure.
// xdmp.invokeFunction requires that invoked functions have
// an arity of zero.
var f = (function() {
return fct.apply(null, params);
}).bind(this);
// Allow passing in user name, rather than id
if(options.user) { options.userId = xdmp.user(options.user); delete options.user; }
// Allow the functions themselves to declare their transaction mode
if(fct.transactionMode && !(options.transactionMode)) { options.transactionMode = fct.transactionMode; }
var result = xdmp.invokeFunction(f, options); // xdmp.invokeFunction returns a ValueIterator
switch(returnType) {
case 'one':
// return fn.head(result); // 8.0-5
return result.next().value;
case 'many':
return result.toArray();
case 'iterable':
default:
return result;
}
}
}
I have a user specified JSON object that I'm attempting to process in the browser.
The problem is that it needs to match an existing object.
They can't accidentally:
forget to include some fields.
typo fields or deliberately add new fields.
Is there a way to handle this?
so basically if I have an object with foo and bar members, I want their defaults if the user's json is just {} ... and if they accidentally send something like {bart: "asdf";} (typo on 'bar') then I want it to generate an exception.
var default_object = { ... };
var allowed_keys = [ "key1", "key2", ... ];
var new_object = default_object.clone();
for (var key in json_object) {
if (json_object.hasOwnProperty(key)) {
if (allowed_keys.indexOf(key) == -1) {
// Report error here
} else {
new_object[key] = json_object[key];
}
}
}
See here for how to write the clone method I used above. If you use jQuery, you could simplify some of this code by using $.extend().