I want to perform more logic before writing an element to an array:
tempDatensatz.push( () => {
var current = window.dataForDataTable[i].outbounds[key].updatedAt;
if (current) {
return current.toString();
} else {
return "".toString();
}
});
Getting the value from that array will be achieved like this:
tempDatensatz[0]()
But I want the same logic in it without having a function to call. I need a normal array, where I get a value like this:
tempDatensatz[0]
What can I do instead?
Updated
I published my project to gitHub, you can take a look if you need a better understanding :)
https://github.com/te2020/GoEuro/blob/master/GoEuro/Views/Home/Index.cshtml
Use an immediately invoked function instead of just a function:
tempDatensatz.push( (function(){
var current = window.dataForDataTable[i].outbounds[key].updatedAt;
if (current) {
return current.toString();
} else {
return "".toString();
}
})());
The function will be executed immediatly after it definition, returning the result. So push won't push a reference to that function but instead it will push it returned value (the result).
You can write a proxy as follows:
function makeProxy(array) {
return new Proxy(array, {
get(target, property) {
return !isNaN(property) ? target[property]() : target[property];
}
});
}
const tempDatensatz = [];
const useThisOne = makeProxy(tempDatensatz);
useThisOne.push(() => alert("Hi, Jane!"));
useThisOne[0];
Pushing/writing to the array will work as expected, but retrieving its elements will go through the get handler, which will execute the function.
You could just use an expression, like:
tempDatensatz.push(
(window.dataForDataTable[i].outbounds[key].updatedAt || '').toString();
);
For more complex expressions you can often use the ternary operator. For the above that would look like this:
tempDatensatz.push(
window.dataForDataTable[i].outbounds[key].updatedAt
? window.dataForDataTable[i].outbounds[key].updatedAt.toString()
: ''
);
Your code
When looking at the github code you linked to, you can do all that pushing with this "oneliner":
var tempDatensatz =
['companyId', 'mode', 'duration', 'outboundId', 'journeyId', 'departureTime',
'arrivalTime', 'stops', 'price', 'updatedAt', 'segments']
.map( prop => (window.dataForDataTable[i].outbounds[key][prop] || '').toString() );
Related
I have an object, that takes input from an API call to fill it up.
let MyDog = {
Name: 'Dog',
}
let arrayFunctions;
fetchDogsFunctions(dogAPIUrl).then(res => {
//results is an array that has a list of functions the dog has, like //getStats(), or walkDog()
arrayFunctions = res;
})
Now I want to map through the array results and call the function on my dog like...
arrayFunctions.map(item => {
await MyDog.item(//Params)
})
How can I do this??
Where MyDog is set up from a file, and then depending on the array functions, for each function it programmatically fills in the call to the new function like MyDog.item where item is a variable in an array called "Walk()"
I assumed how your data might have structured. Pls take a look below and you might get the idea of how to call functions dynamically using "await"
const MyDog = {
funA(a) { return Promise.resolve('Function A called with parameter ' + a) }
, funB(b) { return Promise.resolve('Function B called with parameter ' + b) }
}
const arrayFunctions = ['funA', 'funB']
Promise.all(arrayFunctions.map(async (item, i) =>
await MyDog[item](++i))
)
.then(console.log)
I can't see any property name "item" in your MyDog Object
The answer is to do :
MyDog[item]()
within the loop.
I was asking the correct syntax to perform this operation
I have the following piece of code :
accountSelector.executeInParallel('processAccounts', 'postProcess');
function processAccounts() {
return JSON.stringify(syncMasterLists());
}
And instead of having this, I want to be able to pass a value to the processAccounts accounts function.
For the purpose I changed the code so it's looking like this now :
accountSelector.executeInParallel('processAccounts("DE")', 'postProcess');
function processAccounts(arg) {
return JSON.stringify(syncMasterLists());
}
Unfortunately, after introducing the change I started getting the following error :
Cannot find function processAccounts("DE").
I cannot understand am I doing this wrong(and if yes then what is wrong) or it's just something that can't be done.
I cannot understand am I doing this wrong(and if yes then what is
wrong) or it's just something that can't be done.
accountSelector.executeInParallel takes the function name as parameter and execute the same, processAccounts("DE") is not a valid function name or the name of the function that exists.
As per documentation, there is a way to pass optionalInput parameter
The input, if specified by optionalInput, will be passed into the
function specified by functionName
accountSelector.executeInParallel(functionName, optionalCallbackFunctionName, optionalInput)
In your case, it will be
accountSelector.executeInParallel('processAccounts', 'postProcess', 'DE' );
Why don't you call the function first and replace the result in the 'executeInParallel' method as follows:
var res = processAccounts("DE");
accountSelector.executeInParallel(res, 'postProcess');
function processAccounts(arg) {
return JSON.stringify(syncMasterLists());
}
Some closure can probably solve your problem, depends on how accountSelector.executeInParallel is implemented
const accountSelector = {
executeInParallel(pre, post) {
let result = eval(pre)()
eval(post)(result)
}
}
accountSelector.executeInParallel(processAccountsWithArg('Foo'), 'postProcess');
function processAccount(arg) {
console.log('processAccount', arg)
return JSON.stringify({
key: 'value'
});
}
function processAccountsWithArg(arg) {
return function() {
return processAccount(arg)
}
}
function postProcess(result) {
console.log('postProcess', result)
}
I am new to RxJS and I am trying to write an app that will accomplish the following things:
On load, make an AJAX request (faked as fetchItems() for simplicity) to fetch a list of items.
Every second after that, make an AJAX request to get the items.
When checking for new items, ONLY items changed after the most recent timestamp should be returned.
There shouldn't be any state external to the observables.
My first attempt was very straight forward and met goals 1, 2 and 4.
var data$ = Rx.Observable.interval(1000)
.startWith('run right away')
.map(function() {
// `fetchItems(modifiedSince)` returns an array of items modified after `modifiedSince`, but
// I am not yet tracking the `modifiedSince` timestamp yet so all items will always be returned
return fetchItems();
});
Now I'm excited, that was easy, it can't be that much harder to meet goal 3...several hours later this is where I am at:
var modifiedSince = null;
var data$ = Rx.Observable.interval(1000)
.startWith('run right away')
.flatMap(function() {
// `fetchItems(modifiedSince)` returns an array of items modified after `modifiedSince`
return fetchItems(modifiedSince);
})
.do(function(item) {
if(item.updatedAt > modifiedSince) {
modifiedSince = item.updatedAt;
}
})
.scan(function(previous, current) {
previous.push(current);
return previous;
}, []);
This solves goal 3, but regresses on goal 4. I am now storing state outside of the observable.
I'm assuming that global modifiedSince and the .do() block aren't the best way of accomplishing this. Any guidance would be greatly appreciated.
EDIT: hopefully clarified what I am looking for with this question.
Here is another solution which does not use closure or 'external state'.
I made the following hypothesis :
fetchItems returns a Rx.Observable of items, i.e. not an array of items
It makes use of the expand operator which allows to emit values which follow a recursive relationship of the type x_n+1 = f(x_n). You pass x_n+1 by returning an observable which emits that value, for instance Rx.Observable.return(x_n+1) and you can finish the recursion by returning Rx.Observable.empty(). Here it seems that you don't have an ending condition so this will run forever.
scan also allows to emit values following a recursive relationship (x_n+1 = f(x_n, y_n)). The difference is that scan forces you to use a syncronous function (so x_n+1 is synchronized with y_n), while with expand you can use an asynchronous function in the form of an observable.
Code is not tested, so keep me updated if this works or not.
Relevant documentation : expand, combineLatest
var modifiedSinceInitValue = // put your date here
var polling_frequency = // put your value here
var initial_state = {modifiedSince: modifiedSinceInitValue, itemArray : []}
function max(property) {
return function (acc, current) {
acc = current[property] > acc ? current[property] : acc;
}
}
var data$ = Rx.Observable.return(initial_state)
.expand (function(state){
return fetchItem(state.modifiedSince)
.toArray()
.combineLatest(Rx.Observable.interval(polling_frequency).take(1),
function (itemArray, _) {
return {
modifiedSince : itemArray.reduce(max('updatedAt'), modifiedSinceInitValue),
itemArray : itemArray
}
}
})
You seem to mean that modifiedSince is part of the state you carry, so it should appear in the scan. Why don-t you move the action in do into the scan too?. Your seed would then be {modifiedSince: null, itemArray: []}.
Errr, I just thought that this might not work, as you need to feed modifiedSince back to the fetchItem function which is upstream. Don't you have a cycle here? That means you would have to use a subject to break that cycle. Alternatively you can try to keep modifiedSince encapsulated in a closure. Something like
function pollItems (fetchItems, polling_frequency) {
var modifiedSince = null;
var data$ = Rx.Observable.interval(polling_frequency)
.startWith('run right away')
.flatMap(function() {
// `fetchItems(modifiedSince)` returns an array of items modified after `modifiedSince`
return fetchItems(modifiedSince);
})
.do(function(item) {
if(item.updatedAt > modifiedSince) {
modifiedSince = item.updatedAt;
}
})
.scan(function(previous, current) {
previous.push(current);
return previous;
}, []);
return data$;
}
I have to run out to celebrate the new year, if that does not work, I can give another try later (maybe using the expand operator, the other version of scan).
How about this:
var interval = 1000;
function fetchItems() {
return items;
}
var data$ = Rx.Observable.interval(interval)
.map(function() { return fetchItems(); })
.filter(function(x) {return x.lastModified > Date.now() - interval}
.skip(1)
.startWith(fetchItems());
That should filter the source only for new items, plus start you off with the full collection. Just write the filter function to be appropriate for your data source.
Or by passing an argument to fetchItems:
var interval = 1000;
function fetchItems(modifiedSince) {
var retVal = modifiedSince ? items.filter( function(x) {return x.lastModified > modifiedSince}) : items
return retVal;
}
var data$ = Rx.Observable.interval(interval)
.map(function() { return fetchItems(Date.now() - interval); })
.skip(1)
.startWith(fetchItems());
I use the following code which is working great but I wonder if in JS there is a way to avoid the if and to do it inside the loop, I want to use also lodash if it helps
for (provider in config.providers[0]) {
if (provider === "save") {
....
You can chain calls together using _.chain, filter by a value, and then use each to call a function for each filtered result. However, you have to add a final .value() call at the end for it to evaluate the expression you just built.
I'd argue that for short, simple conditional blocks, an if statement is easier and more readable. I'd use lodash- and more specifically chaining- if you are combining multiple operations or performing sophisticated filtering, sorting, etc. over an object or collection.
var providers = ['hello', 'world', 'save'];
_.chain(providers)
.filter(function(provider) {
return provider === 'save';
}).each(function(p) {
document.write(p); // your code here
}).value();
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.8.0/lodash.js"></script>
Edit: My mistake; filter does not have an overload where you can just supply a literal value. If you want to do literal value checking you have to supply a function as in my amended answer above.
I'd argue that what you have there is pretty good, clean and readable, but since you mentioned lodash, I will give it a try.
_.each(_.filter(config.providers[0], p => p === 'save'), p => {
// Do something with p
...
});
Note that the arrow function/lambda of ECMAScript 6 doesn't come to Chrome until version 45.
Basically, you are testing to see if config.providers[0], which is an object, contains a property called save (or some other dynamic value, I'm using a variable called provider to store that value in my example code below).
You can use this instead of using a for .. in .. loop:
var provider = 'save';
if (config.providers[0][provider] !== undefined) {
...
}
Or using #initialxy's (better!) suggestion:
if (provider in config.providers[0]) {
...
}
How about:
for (provider in config.providers[0].filter(function(a) {return a === "save"}) {
...
}
Strategy, you are looking for some kind of strategy pattern as,
Currenlty the save is hardcoded but what will you do if its coming from other varible – Al Bundy
var actions = {
save: function() {
alert('saved with args: ' + JSON.stringify(arguments))
},
delete: function() {
alert('deleted')
},
default: function() {
alert('action not supported')
}
}
var config = {
providers: [{
'save': function() {
return {
action: 'save',
args: 'some arguments'
}
},
notSupported: function() {}
}]
}
for (provider in config.providers[0]) {
(actions[provider] || actions['default'])(config.providers[0][provider]())
}
Push „Run code snippet” button will shows two pop-ups - be carefull
It is not clearly stated by the original poster whether the desired output
should be a single save - or an array containing all occurrences of
save.
This answer shows a solution to the latter case.
const providers = ['save', 'hello', 'world', 'save'];
const saves = [];
_.forEach(_.filter(providers, elem => { return elem==='save' }),
provider => { saves.push(provider); });
console.log(saves);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.19/lodash.js"></script>
RequestPager sends all the attributes in server_api to the request as query string. However, sometime I want to exclude a parameter on some condition. This is how, i'm setting the param:
server_api: {
query: function () {
return this.searchQuery
},
type: function(){ return this.searchType }
}
If this.searchQuery is empty, it makes the URL like ?query=&type=1. But I don't want to send query or type when it's empty or when my some other condition fails.
I know the dirty way like:
if(!myCollection.searchQuery){
delete(myCollection.server_api.licensed);
}
But this is not maintainable. Because text time I've to create this function. So, I'm looking for a better way of doing this. Any Help?
If you look at how server_api is used:
_.each(_.result(self, "server_api"), function(value, key){
if( _.isFunction(value) ) {
value = _.bind(value, self);
value = value();
}
queryAttributes[key] = value;
});
you'll see that it uses _.result:
result _.result(object, property)
If the value of the named property is a function then invoke it;
otherwise, return it.
var object = {cheese: 'crumpets', stuff: function(){ return 'nonsense'; }};
_.result(object, 'cheese');
=> "crumpets"
_.result(object, 'stuff');
=> "nonsense"
That means that you can make server_api a function which returns the appropriate object.