I have a function where I have to return for each "subcontractor" its response for each selection criteria.
Subcontractor object contains a selectionCriteria object. selectionCriteria object contains an array of data for each selectionCriteria a user has responded to.
Each array item is an object, that contains files, id, request (object that contains info about selection criteria user is responding to), response (contains value of the response).
Here is an example of how a subcontractor looks:
This is the function I come up with, but it's quite complex:
const { subcontractors } = useLoaderData<typeof loader>();
const { t } = useTranslation();
const submittedSubcontractors = subcontractors.filter(
(s) => s.status === 'submitted'
);
const subcontractorsResponsesToSelectionCriteria: Array<ISubcontractor> = [];
let providedAnswersResponded: boolean | null = null;
let providedAnswersFiles: Array<IFile> | [] = [];
let providedAnswersRequiresFiles: boolean | null = null;
submittedSubcontractors.forEach((u) => {
u.selectionCriteria.forEach((c) => {
if (c.request.id === criteriaId) {
if (c.response && 'answer' in c.response) {
if (typeof c.response.answer === 'boolean') {
providedAnswersResponded = c.response.answer;
} else {
providedAnswersResponded = null;
}
} else {
providedAnswersResponded = null;
}
providedAnswersFiles = c.files;
providedAnswersRequiresFiles = c.request.are_files_required;
subcontractorsResponsesToSelectionCriteria.push(u as ISubcontractor);
}
});
});
How could I simplify this code by using .reduce() method, or maybe even better ideas?
You should start working on reducing the level of nesting in your if/else like so:
function getProvidedAnswersResponded(response: any) {
if (response && ('answer' in response) && (typeof response.answer === 'boolean')) {
return response.answer;
}
return null;
}
submittedSubcontractors.forEach(u => {
u.selectionCriteria.forEach(c => {
if (c.request.id !== criteriaId) {
return;
}
providedAnswersResponded = getProvidedAnswersResponded(c.response);
providedAnswersFiles = c.files;
providedAnswersRequiresFiles = c.request.are_files_required;
subcontractorsResponsesToSelectionCriteria.push(u);
});
});
The strategy followed was basically to invert the special cases (such as c.requet.id === criteriaId) and exit the function immediately.
Also, extracting the "provided answer responded" function seems atomic enough to move it to a separate block, giving it more verbosity about what that specific code block is doing.
Original code from the Point of Sale module
In the point_of_sale module there is a list of objects as the following
module.PosModel = Backbone.Model.extend({
models: {
// [...]
{
model: 'pos.session',
fields: ['id', 'journal_ids','name','user_id','config_id','start_at','stop_at','sequence_number','login_number'],
domain: function(self){ return [['state','=','opened'],['user_id','=',self.session.uid]]; },
loaded: function(self,pos_sessions){
self.pos_session = pos_sessions[0];
var orders = self.db.get_orders();
for (var i = 0; i < orders.length; i++) {
self.pos_session.sequence_number = Math.max(self.pos_session.sequence_number, orders[i].data.sequence_number+1);
}
},
},
{
model: 'product.product',
fields: ['display_name', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', 'default_code',
'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description',
'product_tmpl_id'],
domain: [['sale_ok','=',true],['available_in_pos','=',true]],
context: function(self){ return { pricelist: self.pricelist.id, display_default_code: false }; },
loaded: function(self, products){
self.db.add_products(products);
},
// [...]
}
And then the information of the data is loaded like this
load_server_data: function(){
var self = this;
var loaded = new $.Deferred();
var progress = 0;
var progress_step = 1.0 / self.models.length;
var tmp = {}; // this is used to share a temporary state between models loaders
function load_model(index){
if(index >= self.models.length){
loaded.resolve();
}else{
var model = self.models[index];
self.pos_widget.loading_message(_t('Loading')+' '+(model.label || model.model || ''), progress);
var fields = typeof model.fields === 'function' ? model.fields(self,tmp) : model.fields;
var domain = typeof model.domain === 'function' ? model.domain(self,tmp) : model.domain;
var context = typeof model.context === 'function' ? model.context(self,tmp) : model.context;
var ids = typeof model.ids === 'function' ? model.ids(self,tmp) : model.ids;
progress += progress_step;
if( model.model ){
if (model.ids) {
var records = new instance.web.Model(model.model).call('read',[ids,fields],context);
} else {
var records = new instance.web.Model(model.model).query(fields).filter(domain).context(context).all()
}
// [...]
What I have tried. First try
So, I would like to change the domain field of the product.product model. I am trying with this
if (typeof jQuery === 'undefined') { throw new Error('Product multi POS needs jQuery'); }
+function ($) {
'use strict';
openerp.pos_product_multi_shop = function(instance, module) {
var PosModelParent = instance.point_of_sale.PosModel;
instance.point_of_sale.PosModel = instance.point_of_sale.PosModel.extend({
load_server_data: function(){
console.log('-- LOAD SERVER DATA');
var self = this;
self.models.forEach(function(elem) {
if (elem.model == 'product.product') {
// return [['id', 'in', [2]]]; // if I return this domain it works well
domain_loaded = function() {
return new instance.web.Model('product.product').call(
'get_available_in_pos_ids',
[self.pos_session.config_id[0]],
)
}
elem.domain = $.when(domain_loaded);
}
})
var loaded = PosModelParent.prototype.load_server_data.apply(this, arguments);
return loaded;
},
});
}
}(jQuery);
If I return a domain directly it works. But if I replace it with a function that calls a python function with call, the domain is not loaded well: [['sale_ok','=',true],['available_in_pos','=',true]]. I've tried with $.when and without it and it does not work.
In addition elem.domain must be a function because self.pos_session only exists when all the previous model information is executed.
Second try
I have tried this following code as well:
if (elem.model == 'product.product') {
// return [['id', 'in', [2]]]; // if I return the domain like this it works
console.log('>> OLD DOMAIN')
console.log(elem.domain);
elem.domain = function() {
console.log('>>> PRODUCT SESSION');
console.log(self.pos_session);
var product_product_obj = new instance.web.Model('product.product');
return product_product_obj.call(
'get_available_in_pos_ids',
[self.pos_session.config_id[0]],
)
}
console.log('>> NEW DOMAIN')
console.log(elem.domain);
}
So first '>> OLD DOMAIN' is printed, then '>> NEW DOMAIN' and, at last '>>> PRODUCT SESSION' is printed. So the function is executed. But the the domains is not being returned well.
Third try. With "then"
And I cannot use then because I need to do the variable assignation. But on the other hand the assignation is well done becase when I print the new domain the function appears in the log.
Even if I use then I am getting the result well from python
var domain_return = product_product_obj.call(
'get_available_in_pos_ids',
[self.pos_session.config_id[0]],
).then(function(result) {
console.log('>> RESULT: ');
console.log(result)
});
I also tried with other promise, but I get a pending result that is ignored and all the products are shown
elem.domain = function() {
return new Promise(function next(resolve, reject) {
console.log('>>> PRODUCT SESSION');
console.log(self.pos_session);
var product_product_obj = new instance.web.Model('product.product');
var domain_return = product_product_obj.call(
'get_available_in_pos_ids',
[self.pos_session.config_id[0]],
).then(function(result) {
console.log('>> RETURN: ');
console.log(result);
resolve(result);
});
console.log('>> DOMAIN RETURN: ');
console.log(domain_return);
});
}
The rest of the domains of the object are calculated without calling python functions. So I can't copy an example from other place
So, is there a way to avoid the pending result? I cannot use async/await yet.
Maybe to make it syncronous will help but I know this should be avoided
Finally I found a workaround overriding the loaded function where all the products are already loaded
var PosModelParent = instance.point_of_sale.PosModel;
instance.point_of_sale.PosModel = instance.point_of_sale.PosModel.extend({
load_server_data: function(){
let self = this;
self.models.forEach(function(elem) {
if (elem.model == 'product.product') {
elem.fields = ['display_name', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', 'default_code',
'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description',
'product_tmpl_id', 'available_in_pos_ids'];
elem.loaded = function(self, products){
console.log('>> PRODUCTS: ');
console.log(products);
var shop_id = self.pos_session.config_id[0];
var new_products = [];
products.forEach(function(prod) {
if (prod.available_in_pos_ids.includes(shop_id)) {
new_products.push(prod);
}
})
self.db.add_products(new_products);
}
}
})
var loaded = PosModelParent.prototype.load_server_data.apply(this, arguments);
return loaded;
},
});
I'm trying to see if it's possible to add to an already created object on the MicroDB file.
I've tried using .push() but I'm having no luck.
The file has this object in already:
{
"home": {
"location": "walsall",
"time": "12:00",
"date": "12/12/17",
"gym": "home",
"players": ""
}
}
File is a .json
I'm trying to add something into the players key when a user wants to join.
Any help? Also, how would one remove a user name from the players section aforementioned?
Edit:
Some code from my .js file
if (commandEX.toLowerCase() === "join") {
var joinEX = message.content.split(' ')[2];
if (joinEX === undefined) {
return message.reply("**ERROR**: No gym entered, enter a gym to join.").then(m => m.delete(10000));
}
var removeTrigger = message.content.split(' ')[0];
var findGym = message.content.slice(removeTrigger.length);
findGym = findGym.slice(commandEX.length);
findGym = findGym.slice(2);
var commandFind = exRaidDB.data[findGym];
memberEX = message.member.nickname;
if (commandFind == undefined) {
message.channel.send(`**ERROR**: No ex raid at that gym.`).then(m => m.delete(10000));
} else {
message.channel.send(`**Added ${memberEX} to the list, details below.** \n**EX Raid at** ${commandFind.gym} \n**Date**: ${commandFind.date} \n**Time**: ${commandFind.time} \n**Location**: ${commandFind.location}`);
// this line is where the code needs to be for adding the $[message.member.nickname} into the "players" string.
}
}
As you tagged javascript, am I assuming correct you are using Node.js?
If it's the case, you could use fs.writeFileSync(PATH_TO_JSON, JSON.stringify(newObj), 'utf8'); where newObj is the new object you want to store in the .json-file.
But instead of working with the file directly, you can use a simple wrapper like follows:
let
fs = require('fs'),
PATH_TO_JSON = '';
function readJson() {
if (!fs.existsSync(PATH_TO_JSON)) {
writeJson();
}
return JSON.parse(fs.readFileSync(PATH_TO_JSON, 'utf8'));
}
function writeJson(obj = {}) {
fs.writeFileSync(PATH_TO_JSON, JSON.stringify(obj), 'utf8');
}
function Ressource(path) {
PATH_TO_JSON = path;
this.data = readJson();
}
Ressource.prototype.set = function(obj) {
this.data = obj;
};
Ressource.prototype.save = function() {
writeJson(this.data);
};
module.exports = Ressource;
I do not know how can i delete element in localStorage loop
In save method i add element and check for it duplicate
explain please how can i delete element using for example only id or all values
My Factory
.factory('SaveDocuments', function() {
var documents = [];
save: function (id, name, link) {
if(documents.filter(function(a){return a.id==id}).length)
{ alert('conflict!'); }
else {
// add to it,
documents.push({id: id, name: name, link: link});
// then put it back.
localStorage.setItem('document', JSON.stringify(documents));
}
},
del: function(id, name, link) {
if(documents.filter(function(a){return a.id==id}).length) {
for (i = 0; i < localStorage.length; i++){
key = localStorage.key(i);
value = localStorage.getItem(key);
localStorage.removeItem(value);
console.log(value);
break;
}
}
else {
alert('conflict!');
}
}
}
MyController
.controller('PageSearchCtrl', function($scope, ConstSearch, SaveDocuments) {
$scope.saveDocument = function() {
//Create new project
$scope.document = [{"id": 1, "name": "new1", "link": "#/const"}];
SaveDocuments.save($scope.document[0].id,$scope.document[0].name,$scope.document[0].link);
};
$scope.deleteDocument = function () {
$scope.document = [{"id": 1, "name": "new1", "link": "#/const"}];
//Create new project
SaveDocuments.del($scope.document[0].id,$scope.document[0].name,$scope.document[0].link);
}
I recommend changing your service to something like the following:
.factory('SaveDocuments', function () {
var lsKey = 'document', // the key to store the docs in local storage under
documents = JSON.parse(localStorage.getItem(lsKey) || '[]'); // initialise from localStorage
function saveToLocalStorage() {
localStorage.setItem(lsKey, JSON.stringify(documents));
}
return {
save: function (id, name, link) {
if (documents.filter(function (a) {
return a.id == id;
}).length) {
alert('conflict!');
} else {
// add to it,
documents.push({
id: id,
name: name,
link: link
});
saveToLocalStorage();
}
},
del: function (id, name, link) {
// clear all if del() is called with no arguments or null for all args
if (!id && !name && !link) {
documents = [];
saveToLocalStorage();
return;
}
var initialLength = documents.length;
documents = documents.filter(function (doc) {
return (!id || doc.id !== id) && (!name || doc.name !== name) && (!link || doc.link !== link);
});
// if nothing was removed, show error
if (documents.length === initialLength) {
alert('conflict!');
} else {
saveToLocalStorage();
}
}
};
});
Note that I correctly initialised it from the local storage state when the application starts (so when you reload the page the data is there correctly), used a variable to hold the only key you use to store the data in local storage (to keep the code DRY), and fixed your del() method so it keeps ones which don't match the deletion criteria or deletes everything if no arguments passed in, then just overwrites the value in local storage with the updated state.
NB: You should test this, I did not do any testing to see if this works.
In localStorage I have a object records. It has many fields
records[recordId] and I want to be able to delete these entries. Below is my delete function but it does not work. All the records remain attached to the records object in local storage. Not sure what is wrong as I am deleting in what looks like the correct manner. When I print the records object all the records are still there along with their keys????
function deleteLocalRecord(recordId) {
var records = localStorage.getItem('records');
var theRecords;
if (supports_html5_storage()) {
if (records == null) {
theRecords = {};
} else {
// parse the records
theRecords = JSON.parse(records);
}
theRecords[recordId] = ''; // set the record to empty string.
delete theRecords[recordId]; // delete the record
// reset the records object in local storage after change
localStorage.setItem('records', JSON.stringify(theRecords));
}
}
function supports_html5_storage()
{
try
{
return 'localStorage' in window && window['localStorage'] !== null;
}
catch (e)
{
return false;
}
}
Live demo
function deleteLocalRecord(recordId) {
if (localStorageEnabled()) {
var records = localStorage.getItem('records'),
theRecords = JSON.parse(records) || {};
print(JSON.stringify(theRecords)); // before
delete theRecords[recordId]; // delete
print(JSON.stringify(theRecords)); // after
localStorage.setItem('records', JSON.stringify(theRecords));
print(localStorage.getItem("records"));
}
}
var records = {
"test": "test",
"stuff": "stuff"
};
localStorage.setItem("records", JSON.stringify(records));
deleteLocalRecord("test");
function print(x) {
document.body.innerHTML += "<p>" + x + "</p>";
};
function localStorageEnabled() {
return typeof window["localStorage"] !== "undefined";
};