Pass value function - javascript

I have an application that populates the array continuously until it is stopped and it does two things:
If you click Stop button, it writes values in the DB.
Every 1000sec it checks the size of array and if it is > 2000 write the values in the db.
Now I have a problem:
I use the first element of the array to do some calculations, before writing the data to the db.
So if the array exceeds the size of 2000, it performs a splice and passes the array to another page, taking the first element as the basis for the calculation that will be performed on the next page.
At this point, if the user clicks the stop key as the basis for the operations, the last element of the array previously passed must be used.
For example:
array = [0, 20, 40, ......, 2000,..]
array.length > 2000
arrayBase = 0 // I use it for operations.
// Do a splice
array = [2020, 2040, ...... ]
array.length < 2000
//User click stop Button
//I should pass as arrayBase the last value of array (2000)
I hope at least I have explained myself by example.
This is my code:
//this function populate array until I click stop
populateArray(){
this.arrayTimestamp.push(`${buf.readInt16LE(0)}`);
this.firstElementTimestamp = this.arrayTimestamp[0];
//.....
}
//This function check the size and write in the DB if > 2000
checkSize(){
that.timeout = setInterval(function() {
if( (that.arrayTimestamp.length > 2000 ){
that.arrayTimestampCopy = that.arrayTimestamp.splice( 0, 2000 );
//this is a function in other page where I do some operations
scrittura.write({
counterTimestamp: that.firstElementTimestamp,
//...
})
.then(response => {
//...
})
// I have tried something like this:
that.firstElementTimestamp = that.arrayTimestamp[2000] //obviously it is undefined as the array with the splice has been emptied
}, 1000);
}
//this is the function when the Stop button is clicked.
stopConnection(){
Actions.Activity({
counterTimestamp: this.firstElementTimestamp,
//...
})
}
So my goal is to find a way to always use the same base in the calculations, without it being updated.
How can I do?

I think you should use array reduce or Promise All (based on which one you need, parallel or not)
arr.reduce((prom, item) => {
return prom.then(() => {
return scrittura.write(item).then((result) => ... );
});
}, Promise.resolve()).then(function() {
// all done here
}).catch(function(err) {
// error here
});
or use Promise All for parallel
You can see another example here
Synchronous loop in Promise all

Related

save results from one api in different arrays

I have an action in pinia which I do a get to an api where I get some elements, the api returns the pagination with the current page, elements per page, total pages and total elements, the get only returns 20 elements for each Every time I call the action, when I load a component I call it and it returns the data (20 elements) and I save it in an array to then go through it and show it in cards, I have a button where I call the get action with the page in +1 to bring the other 20 elements. I am trying to get all the elements when loading my component, the way to get them all would be by clicking on the button which would return 20 by 20, but how can I make it so that when loading the component it calls all the elements without affect the array that goes from 20 to 20 and save them in a different array? In an array I save 20,40,60..., but in the other I would save the total elements.
>action
fetchElements(query = "", params) {
return new Promise((resolve) => {
this.loading = true;
const index =
stage_amp === undefined || stage_amp === "dev"
? client.initIndex("req-dev")
: client.initIndex("req-prod");
index
.search(query, params)
.then(({ hits, page, hitsPerPage, nbPages, nbHits }) => {
hits.forEach((hit) => {
this.elements.push(hit);
});
this.pagination = {
page: page,
perPage: hitsPerPage,
totalPages: nbPages,
totalHits: nbHits,
};;
});
});
},
>I get the first 20 elements
getElements.fetchElements();
>button that brings me the other 20 elements
const pagination = () => {
getElements.fetchElements(query, { page: getElements.pagination.page + 1});
}
pensaba
As far as I can understand, you want a way to get the 20 elements from the current page, but you still want to fetch all the elements ?
There several ways to this, but the simplest (in my opinion) would be to have an array of all your pages, and an index to keep track of your current page.
For example, the structure may look like this :
// In your store
const state = () => {
elementsPages: [
[...], // elements from page 1
[...], // elements from page 2
[...] // elements from page 3
],
currentPage: 0
}
That way, you could have 2 differents computed, one to get only the 20 elements from your current page, and one to get all the elements
// in your component
export default {
computed: {
currentPageElements() {
const allPages = this.$store.state.elementsPages
const currentPage = this.$store.state.currentPage
return allPages[currentPage]
},
allElements () {
return this.$store.state.elementsPages.flat()
}
}
}
This works assuming that elementsPages is always an array, and currentPage is an Int between 0 and elementsPages.length. The code is just a simplified sample that you may need to adjust to fit your needs.
This is, in my opinion, a good way to manage your store state since you will not duplicate your data (meaning that you don't have a property that contains the first 20 elements, AND another property that contains everything)
Also, note that the elementsPages property is an array of arrays. Instead of :
hits.forEach((hit) => { state.elements.push(hit) })
You may need to use something like this :
state.elementsPages.push(hits)
Hope that helps ;)

How to let Cypress contains return boolean instead of failing the test

I am testing a virtual dropdown list, my code is like this:
while (!cy.contains('.ant-select-item',/^Cypress$/)) {
cy.get('.ant-select-dropdown').trigger('wheel', {deltaX:0,deltaY:100});
}
It keeps wheeling down until finds a specific element. However, this code does not work, when contains does not find the specific element, it fails the test instead of return false.
How to make the while loop work?
You can use jQuery, Cypress.$ instead.
while (!Cypress.$('.ant-select-item:contains('Cypress').length) {
cy.get('.ant-select-dropdown').trigger('wheel', {deltaX:0,deltaY:100});
}
One thing - :contains() will match partially, so this is no good if more than one item has the string.
Long version - Cypress.$('.ant-select-item:contains('Cypress') gets a list of matching elements. If none found, it does not fail but the length of the list is 0. Since 0 is falsy, the loop continues.
The loop idea is only good if the dropdown does actually contain the value somewhere, otherwise it spins forever.
While loops generally don't work with Cypress, it would be safer to use a repeating (recursive) function
function findItem(item, loop = 0) {
if (loop === 10) throw 'Too many attempts'
cy.get('.ant-select-item')
.invoke('text')
.then(textsOfCurrentList => {
if (!textsOfCurrentList.contains(item)) {
// wheel down and try next
cy.get('.ant-select-dropdown').trigger('wheel', {deltaX:0,deltaY:100})
findItem(item, ++loop)
} else {
return
}
})
})
findItem('Cypress')
Or with package cypress-recurse
recurse(
() => cy.get('.ant-select-item').invoke('text'),
(textsOfCurrentList) => {
const found = textsOfCurrentList.contains(item)
if (!found) {
cy.get('.ant-select-dropdown').trigger('wheel', {deltaX:0,deltaY:100})
}
return found // true means finish, false means repeat
},
{
log: true,
limit: 10, // max number of iterations
timeout: 30000, // time limit in ms
},
)
A good background info avoid-while-loops-in-cypress

Remove all keys except current one in foreach, javascript

order.get_orderlines() is an array of three objects.
I want to call the print_xml function everytime I loop on an orderline where product.is_gift_product == true, but I also want to clean the array before.
Each time, I want to remove all other rows except the one I'm looping on.
I tried to remove all key on which I'm looping where product.is_gift_product != true, and when I loop on a orderline on which product.is_gift_product == true, to remove everything after, but this way I'm not printing the same tickets number as of the number of products marked as gift.
// START GIFT PRODUCT MANAGEMENT
var order_saved = order;
order.get_orderlines().forEach( function (orderline, i) {
if (orderline.product.is_gift_product != true) {
order.get_orderlines().splice(i, 1);
order_saved = order;
} else {
order.get_orderlines().splice(i, 9e9);
setTimeout(function(){
self.print_xml_gift(order, 'XmlGiftReceipt');
order._printed = true;
}, 2000);
}
});
// END GIFT PRODUCT MANAGEMENT
Try chaining a .filter(orderLine => orderLine.product.is_gift_product) before calling your forEach.
you should filter all elements to take a gift products.
During filtering, you can call for print of them.
const order_saved = order.get_orderlines().filter(orderline =>
orderline.product.is_gift_product).forEach(() => {
setTimeout(function(){
self.print_xml_gift(order, 'XmlGiftReceipt');
order._printed = true;
}, 2000);
});
I'm not sure if you need this timeout

SAPUI5 Javascript - Get first and last elements of array for each unique property

SAPUI5 - I have an array of objects and one of the properties in those is 'Category'.
For example say I have 2 different types of Category, 'Front Shop' and 'Production Area', what I need to do is to be able to get the first value of each and the last value of each, and then set the enabled property of a button as enabled/disabled.
I'm currently using undercore js (_.each) to loop through to perform some other logic, so can include additional logic here.
Not sure if Underscore has a built in function for this?
Or could someone point me in the right direction on how to do this?
I've got my first pass at what was wanted where I get the very first result and the last result, but now need to set this for each unique category.
Example code below:
// Set view data
oViewData.Questions = oQuestions.results;
oViewData.Questions.TotalNumberOfQuestions = oQuestions.results.length;
// Loop Questions, to get Category Desc and Competency Desc values from relevant Sets
_.each(oViewData.Questions, function (result, index) {
// Read and set Category Desc
this.getView().getModel("Survey").read("/CategorySet", {
filters: [new Filter("CategoryId", FilterOperator.EQ, result.CategoryId)],
success: function (oData) {
oViewData.Questions[index]._CategoryDesc = oData.results[0].CategoryDesc;
this.setViewData(oViewData);
}.bind(this),
error: function (oError) {}.bind(this)
});
// Read and set Competency Desc
this.getView().getModel("Survey").read("/CompetencySet", {
filters: [new Filter("CompetencyId", FilterOperator.EQ, result.CompetencyId)],
success: function (oData) {
oViewData.Questions[index]._CompetencyDesc = oData.results[0].CompetencyDesc;
this.setViewData(oViewData);
}.bind(this),
error: function (oError) {}.bind(this)
});
// Set all move up / down buttons to enabled
oViewData.Questions[index]._MoveUpBtn = true;
oViewData.Questions[index]._MoveDownBtn = true;
// if category id is the first one in the list
}.bind(this));
// Overwrite first move up button and last move down btn to disabled
oViewData.Questions[0]._MoveUpBtn = false;
oViewData.Questions.slice(-1)[0]._MoveDownBtn = false;
// Set view data
this.setViewData(oViewData);
First, you can iterate through arrays with native JavaScript.
_.each(array, function(item) {}) is the same as array.forEach(function(item) {}).
Second, you can use the built-in filter function for your actual question:
const aFrontShopItems = oViewData.Questions.filter(function(oItem) {
return oItem.Category === "Front Shop";
}
If oViewData.Questions is an array then the function passed to filter is applied to every element. If the condition (e.g. oItem.Category === "Front Shop") is true then the element is added to the new array aFrontShopItems. Obviously you need to call filter a second time to get the Production Area items. You can then apply your logic to the first and last items of your new arrays.

Accumulating and resetting values in a stream

I'm playing with Reactive Programming, using RxJS, and stumbled upon something I'm not sure how to solve.
Let's say we implement a vending machine. You insert a coin, select an item, and the machine dispenses an item and returns change. We'll assume that price is always 1 cent, so inserting a quarter (25 cents) should return 24 cents back, and so on.
The "tricky" part is that I'd like to be able to handle cases like user inserting 2 coins and then selecting an item. Or selecting an item without inserting a coin.
It seems natural to implement inserted coins and selected items as streams. We can then introduce some sort of dependency between these 2 actions — merging or zipping or combining latest.
However, I quickly ran into an issue where I'd like coins to be accumulated up until an item is dispensed but not further. AFAIU, this means I can't use sum or scan since there's no way to "reset" previous accumulation at some point.
Here's an example diagram:
coins: ---25---5-----10------------|->
acc: ---25---30----40------------|->
items: ------------foo-----bar-----|->
combined: ---------30,foo--40,bar--|->
change:------------29------39------|->
And a corresponding code:
this.getCoinsStream()
.scan(function(sum, current) { return sum + current })
.combineLatest(this.getSelectedItemsStream())
.subscribe(function(cents, item) {
dispenseItem(item);
dispenseChange(cents - 1);
});
25 and 5 cents were inserted and then "foo" item was selected. Accumulating coins and then combining latest would lead to "foo" being combined with "30" (which is correct) and then "bar" with "40" (which is incorrect; should be "bar" and "10").
I looked through all of the methods for grouping and filtering and don't see anything that I can use.
An alternative solution I could use is to accumulate coins separately. But this introduces state outside of a stream and I'd really like to avoid that:
var centsDeposited = 0;
this.getCoinsStream().subscribe(function(cents) {
return centsDeposited += cents;
});
this.getSelectedItemsStream().subscribe(function(item) {
dispenseItem(item);
dispenseChange(centsDeposited - 1);
centsDeposited = 0;
});
Moreover, this doesn't allow for making streams dependent on each other, such as to wait for coin to be inserted until selected action can return an item.
Am I missing already existing method? What's the best way to achieve something like this — accumulating values up until the moment when they need to be merged with another stream, but also waiting for at least 1 value in 1st stream before merging it with the one from the 2nd?
You could use your scan/combineLatest approach and then finish the stream with a first followed up with a repeat so that it "starts over" the stream but your Observers would not see it.
var coinStream = Rx.Observable.merge(
Rx.Observable.fromEvent($('#add5'), 'click').map(5),
Rx.Observable.fromEvent($('#add10'), 'click').map(10),
Rx.Observable.fromEvent($('#add25'), 'click').map(25)
);
var selectedStream = Rx.Observable.merge(
Rx.Observable.fromEvent($('#coke'), 'click').map('Coke'),
Rx.Observable.fromEvent($('#sprite'), 'click').map('sprite')
);
var $selection = $('#selection');
var $change = $('#change');
function dispense(selection) {
$selection.text('Dispensed: ' + selection);
console.log("Dispensing Drink: " + selection);
}
function dispenseChange(change) {
$change.text('Dispensed change: ' + change);
console.log("Dispensing Change: " + change);
}
var dispenser = coinStream.scan(function(acc, delta) { return acc + delta; }, 0)
.combineLatest(selectedStream,
function(coins, selection) {
return {coins : coins, selection : selection};
})
//Combine latest won't emit until both Observables have a value
//so you can safely get the first which will be the point that
//both Observables have emitted.
.first()
//First will complete the stream above so use repeat
//to resubscribe to the stream transparently
//You could also do this conditionally with while or doWhile
.repeat()
//If you only will subscribe once, then you won't need this but
//here I am showing how to do it with two subscribers
.publish();
//Dole out the change
dispenser.pluck('coins')
.map(function(c) { return c - 1;})
.subscribe(dispenseChange);
//Get the selection for dispensation
dispenser.pluck('selection').subscribe(dispense);
//Wire it up
dispenser.connect();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.6/rx.all.js"></script>
<button id="coke">Coke</button>
<button id="sprite">Sprite</button>
<button id="add5">5</button>
<button id="add10">10</button>
<button id="add25">25</button>
<div id="change"></div>
<div id="selection"></div>
Generally speaking you have the following set of equations:
inserted_coins :: independent source
items :: independent source
accumulated_coins :: sum(inserted_coins)
accumulated_paid :: sum(price(items))
change :: accumulated_coins - accumulated_paid
coins_in_machine :: when items : 0, when inserted_coins : sum(inserted_coins) starting after last emission of item
The hard part is coins_in_machine. You need to switch the source observable based on some emissions from two sources.
function emits ( who ) {
return function ( x ) { console.log([who, ": "].join(" ") + x);};
}
function sum ( a, b ) {return a + b;}
var inserted_coins = Rx.Observable.fromEvent(document.getElementById("insert"), 'click').map(function ( x ) {return 15;});
var items = Rx.Observable.fromEvent(document.getElementById("item"), 'click').map(function ( x ) {return "snickers";});
console.log("running");
var accumulated_coins = inserted_coins.scan(sum);
var coins_in_machine =
Rx.Observable.merge(
items.tap(emits("items")).map(function ( x ) {return {value : x, flag : 1};}),
inserted_coins.tap(emits("coins inserted ")).map(function ( x ) {return {value : x, flag : 0};}))
.distinctUntilChanged(function(x){return x.flag;})
.flatMapLatest(function ( x ) {
switch (x.flag) {
case 1 :
return Rx.Observable.just(0);
case 0 :
return inserted_coins.scan(sum, x.value).startWith(x.value);
}
}
).startWith(0);
coins_in_machine.subscribe(emits("coins in machine"));
jsbin : http://jsbin.com/mejoneteyo/edit?html,js,console,output
[UPDATE]
Explanations:
We merge the insert_coins stream with the items stream while attaching a flag to them to know which one of the two emitted when we receive a value in the merged stream
When it is the items stream emitting, we want to put 0 in coins_in_machine. When it is the the insert_coins we want to sum the incoming values, as that sum will represent the new amount of coins in the machine. That means the definition of insert_coins switches from one stream to another under the logic defined before. That logic is what is implemented in the switchMapLatest.
I use switchMapLatest and not not switchMap as otherwise the coins_in_machine stream would continue to receive emission from former switched streams, i.e. duplicated emission as in the end there are ever only two streams to and from which we switch. If I may, I would say this is a close and switch that we need.
switchMapLatest has to return a stream, so we jump through hoops to make a stream that emits 0 and never ends (and does not block the computer, as using the repeat operator would in that case)
we jump through some extra hoops to make the inserted_coins emit the values we want. My first implementation was inserted_coins.scan(sum,0) and that never worked. The key and I found that quite tricky, is that when we get to that point in the flow, inserted_coins already emitted one of the values that is a part of the sum. That value is the one passed as a parameter of flatMapLatest but it is not in the source anymore, so calling scan after the fact won-t get it, so it is necessary to get that value from the flatMapLatest and reconstitute the correct behaviour.
You can also use Window to group together multiple coin events, and use item selection as the window boundary.
Next we can use zip to acquire the item value.
Notice we instantly try to give out items. So the user does have to insert coins before he decide on an item.
Notice i decided to publish both selectedStream and dispenser for safety reasons, we don't want to cause a race-condition where events fire while we're building up the query and zip becomes unbalanced. That would be a very rare condition, but notice that when our sources had been cold Observables, they pretty much start generating as soon as we subscribe, and we must use Publish to safeguard ourselves.
(Shamelessly stolen paulpdaniels example code).
var coinStream = Rx.Observable.merge(
Rx.Observable.fromEvent($('#add5'), 'click').map(5),
Rx.Observable.fromEvent($('#add10'), 'click').map(10),
Rx.Observable.fromEvent($('#add25'), 'click').map(25)
);
var selectedStream = Rx.Observable.merge(
Rx.Observable.fromEvent($('#coke'), 'click').map('Coke'),
Rx.Observable.fromEvent($('#sprite'), 'click').map('Sprite')
).publish();
var $selection = $('#selection');
var $change = $('#change');
function dispense(selection) {
$selection.text('Dispensed: ' + selection);
console.log("Dispensing Drink: " + selection);
}
function dispenseChange(change) {
$change.text('Dispensed change: ' + change);
console.log("Dispensing Change: " + change);
}
// Build the query.
var dispenser = Rx.Observable.zip(
coinStream
.window(selectedStream)
.flatMap(ob => ob.reduce((acc, cur) => acc + cur, 0)),
selectedStream,
(coins, selection) => ({coins : coins, selection: selection})
).filter(pay => pay.coins != 0) // Do not give out items if there are no coins.
.publish();
var dispose = new Rx.CompositeDisposable(
//Dole out the change
dispenser
.pluck('coins')
.map(function(c) { return c - 1;})
.subscribe(dispenseChange),
//Get the selection for dispensation
dispenser
.pluck('selection')
.subscribe(dispense),
//Wire it up
dispenser.connect(),
selectedStream.connect()
);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.6/rx.all.js"></script>
<button id="coke">Coke</button>
<button id="sprite">Sprite</button>
<button id="add5">5</button>
<button id="add10">10</button>
<button id="add25">25</button>
<div id="change"></div>
<div id="selection"></div>

Categories

Resources