Remove all keys except current one in foreach, javascript - 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

Related

Javascript filter with multiple criteria

I am filtering items based off of multiple criteria. in my sample I have three different criteria but the final code will have 5 different criteria. My current code works but it is not efficient and now that I have added a third criteria it is getting blotted. I know there has to be a better more efficient way to write the code.
My three filters are: Search, Year & Weeks.
return this.list.filter((item) =>{
let results
// if search only
if(this.search) {
results = item.title.toLowerCase().match(this.search.toLowerCase())
}
// if a year is selected only
if(this.whichYear) {
results = this.checkForDefaultYear(item).includes(this.whichYear)
}
// if num of weeks only
if(this.numOfWeeks) {
results = item.units.length === this.numOfWeeks
}
// if both search and year
if(this.search && this.whichYear) {
results = this.checkForDefaultYear(item).includes(this.whichYear) && item.title.toLowerCase().match(this.search.toLowerCase())
}
// if neither search or year
if(!this.search && !this.whichYear && !this.numOfWeeks)
results = item.title
return results
})
The below above works but now that I have added Weeks I have to do something like
if(this.search && this.whichYear && this.numOfWeeks) {}
if(this.search && this.whichYear) {}
if(this.search && this.numOfWeeks) {}
if(this.whichYear && this.numOfWeeks) {}
This is going to get out of control once I have all 5 filters. This does not seem like the best way to write this code.
I'm not sure if I understand correctly, but from what I understand you want to run a series of checks together, depending on whether a user selected certain options or not.
To do so, you can try with this approach:
return this.list.filter((item) =>{
let results = !!item.title; // default filter
// if previous filters and search
if(this.search) {
results = results && item.title.toLowerCase().match(this.search.toLowerCase());
}
// if previous filters and year
if(this.whichYear) {
results = results && this.checkForDefaultYear(item).includes(this.whichYear)
}
// if previous filters and weeks
if(this.numOfWeeks) {
results = results && item.units.length === this.numOfWeeks
}
return results;
});
Here, it works like this:
Apply default filter, in this case, the title must be not empty
If a user selected the search filter, you apply an additional condition to the previous ones (here only default)
If a user selected the whichYear filter, you apply an additional condition to the previous ones (here default and search)
If a user selected the numOfWeeks filter, you apply an additional condition to the previous ones (here default, search, and year)
So you basically extend the conditions chain depending on what your user selects. And this way you can add more filters according to your needs.
You could also do a similar thing using arrays: create a filters array and push() a filter with every condition and in the end, check if every filter returned true.
The filter callback needs a true or false value.
So the most common way is to return false if a check fails, end return true if it's correct :
return this.list.filter((item) =>{
if (condition1) {
return false; // Invalid
}
if (condition2) {
return false; // Invalid
}
// Valid
return true;
})
So in your case, you can do somthing like:
return this.list.filter((item) =>{
// if search only
if (item.title.toLowerCase().match(this.search.toLowerCase()) {
return false
}
// if a year is selected only
if (this.checkForDefaultYear(item).includes(this.whichYear)) {
return false
}
// if num of weeks only
if (item.units.length === this.numOfWeeks) {
return false
}
// ...and the rest...
// Valid
return true;
})

Pass value function

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

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.

Implementing Infinite Scrolling with Firebase?

var self = this;
var firebaseRef = new Firebase(baseUrl + '/sparks');
firebaseRef.limitToLast(5).on('child_added', function(childSnapshot, prevChildKey) {
self.addChild(childSnapshot); // adds post to a <div>
});
My code currently loads the last 5 posts and will load any new posts. However, I'd also like to be able to load older posts as well. I have a button that when clicked will call a function (that I'm unsure of how to implement) that loads older posts. How do I retrieve these older posts?
(The arrow just signifies that I want to retrieve posts starting from the bottom and working my way up to the top)
You need to think a bit backwards to do this. When you get the results for your query for the first page, remember the first item in the results:
firebaseRef.endAt().limitToLast(5).on('child_added', function(childSnapshot, prevChildKey) {
self.addChild(childSnapshot); // adds post to a <div>
});
While you cannot access child items by index with Firebase, you can store the key of an item and use that to start a next query.
var firstKnownKey;
firebaseRef.orderByKey().limitToLast(5).on('child_added', function(childSnapshot, prevChildKey) {
if (!firstKnownKey) {
firstKnownKey = childSnapshot.key;
}
self.addChild(childSnapshot); // adds post to a <div>
});
Now you have a variable firstKnownKey that has the first key you've ever seen. To get the previous batch of children, you pass that value in to endAt() when you fire your next query:
firebaseRef.orderByKey().endAt(firstKnownKey).limitToLast(5).on('child_added', function(childSnapshot, prevChildKey) {
if (!firstKnownKey) {
firstKnownKey = childSnapshot.key;
}
self.addChild(childSnapshot); // adds post to a <div>
});
Answers to similar questions of the past few days:
Can I get the nth item of a firebase "query"?
Firebase results range using startAt and endAt
Since endAt() is inclusive, the last item gets repeated every time I do the infinite scroll, so I did a little modification to frank van puffen 's answer.
I initiate a list childrenVal to store all the values, another list childrenKey to store all the keys and a var firstKnownKey, as frank van puffen sugests.
var childrenVal=[];
var childrenKey=[];
var firstKnownKey = "";
For the first time you make the query, you get the last 5 elements:
getFirst(){
firebaseRef.orderByKey().limitToLast(5).once('value')
.then((snap)=>{
snap.forEach(childSnap => {
childrenVal.unshift(childSnap.val());
childrenKey.unshift(childSnap.key);
});
firstKnownKey = childrenKey[childrenKey.length-1];
});
}
In your next query, you won't want your firstKnownKey to get repeated, so I did the following function:
exclude(key){
return key.substring(0, key.length - 1) + String.fromCharCode(key.charCodeAt(key.length - 1) - 1)
}
and for the query itself, the following function:
getNext() {
firebaseRef.orderByKey().endAt(exclude(firstKnownKey)).limitToLast(5).once('value')
.then((snap) => {
snap.forEach(childSnap => {
childrenVal.unshift(childSnap.val());
childrenKey.unshift(childSnap.key);
});
firstKnownKey = childrenKey[childrenKey.length - 1];
});
}

Json and group by solution

I'm trying to select the last messages of an inbox and group them in a list by the topic_id. i want to display the last message of each topic.
the array looks like this:
[{
"id":"5",
"topic_id":"4",
"message_from":"24",
"message":"how do you do?",
"date":"2015-01-13 15:34:59"
},
{
"id":"6",
"topic_id":"1",
"message_from":"33",
"message":"go go go!!",
"date":"2015-01-13 13:35:06"
},
{
"id":"7",
"topic_id":"4",
"message_from":"33",
"message":"Je suis charlie",
"date":"2015-01-14 16:24:46"
},....
is there a solution to do it without a loop?
You can't do this without loops, but you can make this easier by breaking down the sequence of events into smaller functions. You might not like this approach, but it's the cleanest imo. Alternatively you can use a third-party library (underscore, perhaps?) that allows you to run groupings on data.
Basically, get a list of all the topic_ids for all records, loop over that topic_id array and pull out the last record for each and add that to an output array.
// Get a list of all the topic ids - no duplicates
function getTopicIds(arr) {
var out = [];
arr.forEach(function (el) {
if (out.indexOf(el.topic_id) === -1) out.push(el.topic_id);
});
return out;
}
// Given a topic_id, filter the array for only those records
// sort in desc order by id, and return the first record.
// Given that each record has a unique id, and we know that older
// messages will have higher ids, it's easier to sort by id than
// date here
function getLastMsg(id, arr) {
return arr.filter(function (el) {
return el.topic_id === id;
}).sort(function (a, b) { return +b.id - +a.id; })[0];
}
// return a array of the last messages for each topic_id
// in the records array
function getLastMsgs(arr) {
return getTopicIds(arr).map(function (id) {
return getLastMsg(id, arr);
});
}
var result = getLastMsgs(arr);
DEMO

Categories

Resources