Overwriting a key, if it exists, in a key/value array - javascript

I'm allowing users to highlight rows in a table by clicking them.
When they click them, HighToggle() is called.
HighToggle() checks to to see if the bgcolor of the td matches the highlighter color and either highlights or un-highlights.
function HighToggle(cObj,vid,vColor,hColor) {
if ((vColor == hColor)) {
$(cObj).css('background-color','#FFFFFF'); // un-highlight
vx.push({'id' : vid, 'status' : 'off'});
} else {
$(cObj).css('background-color','#' + Highlighter.Hex); // highlight
vx.push({'id' : vid, 'status' : 'on'});
}
}
It is important that I store these in an array, because I have recordset navigation that deletes the rows (from view) and loads in new rows via JSON. Otherwise, when I wanted to submit the data to the server, I could just loop through table rows and build the array pre-submit.
So, that works fine, except that it adds duplicate id's in the case of each time something is highlighted or un-highlighted, so it might look like:
1: on, 7: on, 7: off, 2: on, 4: on, 1: off, 3: on, 1: on, 2: off.
Which is just a mess.
Following the same click process that results in that array above, what I'd like to end up with is..
1: on, 7: off, 2: off, 4: on, 3: on
Rows never interacted with (5, 6, 8, 9, 10 if the result set only had 10 rows), need not have an entry in the array at all.
I'm looking for a fast way to search my array and override rows if they exist.
I have thought of one solution that I haven't tried to implement yet because I feel like there might be something better.
I could make a separate array, let's say it's called vindexes. Vindexes could use the ID passed as the vid variable like so..
vindexes[vid] = vx.length;
HighToggle could check to see if vindexes[vid] exists, and if so, use that id to override rather than vx.push.
It would work (wouldn't it?) but is it reinventing the wheel? Is there a faster way? This function will be used a lot on each page.
I've found the grep function, since I'm using jQuery, but I don't think grep is applicable to me here since I want to know the index of where the ID was found.
Thank you for any advice.
Edit: Here's what I devised myself, though I think that the answer is a more elegant and sensible solution.
function HighToggle(cObj,vid,vColor,hColor) {
vid = Number(vid);
if (!(vid in vi)) {
inIndex = vx.length;
} else {
inIndex = vi[vid];
}
if ((vColor == hColor)) {
$(cObj).css('background-color','#FFFFFF');
vi[vid] = inIndex;
vx[inIndex] = {'id' : vid, 'status' : 'off'};
} else {
vi[vid] = inIndex;
$(cObj).css('background-color','#' + Highlighter.Hex);
vx[inIndex] = {'id' : vid, 'status' : 'on'};
}
}
My solution, and the answer are both effective for my use, but perhaps someone else will run into this situation and benefit from the code I've provided.

You could use an object:
var vx = {};
function HighToggle(cObj,vid,vColor,hColor) {
if ((vColor == hColor)) {
$(cObj).css('background-color','#FFFFFF'); // un-highlight
vx[vid] = "off";
} else {
$(cObj).css('background-color','#' + Highlighter.Hex); // highlight
vx[vid] = "on";
}
}
Then if you need an array later (for submitting to server, etc):
var vxArray = [];
for (var key in vx) {
vxArray.push({id: key, status: vx[key]});
}

Related

Js/React : Find ONLY the elements whitin an array that matches X condition to get X results

Im getting different elements from an array that comes from the backend like so :
data = [{id: '1', collection: 32, isNew: true},{id: '5', collection: 22, isNew: false}, .... ]
The user is allowed to select in a massive way differents Ids, and edit them, and I need to show a different modal for each different cases :
The conditions to show the differents modals are based on : If I select only the ones that has isNew = true, if I select only the ones with isNew= false, or if I select both Id cases isNew=false and isNew=true.
So far Ive tried this :
const getNewInfo = data.some(item => item.isNew)
if(getNewInfo) {
return this.explainNewInfo(true)
} else if(!getNewInfo) {
return this.explainNewInfo(false)
} else {
return this.explainNewInfo()
}
I also tried with the filter method and push to a new array, but it relies on the same logic at the end. For the first both cases, it works fine, but in the 3rd case, I cant get in, since whenever matches that isNew is true, it gets in there, and discard all the rest of posibilites.
Each function that is being called on the if else, is a function that recives or not a parameter indicating if is necessary to show a certain modal for the 3 differentes cases.
You can use every function on an array to check for all objects. Read about every.
const isAllNewInfo = data.every(item => item.isNew)
const isAllOldInfo = data.every(item => !item.isNew)
if (isAllNewInfo) {
return this.explainNewInfo(true)
} else if(isAllOldInfo) {
return this.explainNewInfo(false)
} else {
return this.explainNewInfo()
}

How do I update the value of a custom attribute (enum-of-strings) in SFCC via code?

Requirement: I want to update the value of a custom attribute (name: badges) (type: enum-of-strings) for a Product via code. I want to set the value "bestSeller" as selected. How should I do that update because the code below is not working?
Screenshot of the Custom Attribute in Business Manager
Code snippet:
function updateBestSeller() {
var ProductMgr = require('dw/catalog/ProductMgr');
var Site = require('dw/system/Site');
var UUIDUtils = require('dw/util/UUIDUtils');
var CustomObjectMgr = require('dw/object/CustomObjectMgr');
var currentSite = Site.getCurrent();
var bestSellerOrderUnits = Object.hasOwnProperty.call(currentSite.preferences.custom, 'bestSellerOrderUnits') ? currentSite.getCustomPreferenceValue('bestSellerOrderUnits') : 0;
try {
Transaction.wrap(function () {
var count = 1;
var products = ProductMgr.queryAllSiteProducts();sni
var HashSet = require('dw/util/HashSet');
var badges = new HashSet();
if (products.count > 0) {
while (products.hasNext() && count < 5) {
var product = products.next();
var badges = [];
badges.push('bestSeller');
if (Object.hasOwnProperty.call(product.custom, 'badges')) {
product.custom.badges = badges
}
count++;
Logger.debug('{0}',product.ID);
}
}
products.close();
});
} catch (ex) {
Logger.error(ex.toString());
return new Status(Status.ERROR, 'ERROR', 'UPDATE failed');
}
return new Status(Status.OK, 'OK', 'UPDATE successful');
}
I think what is probably happening here is that your attempt to validate that a product has a badges key in the product.custom attribute collection is failing. This prevents the update from occurring.
I suggest removing the condition around the following line:
product.custom.badges = badges;
If that line were to not execute, then the update to the database would never occur.
The way custom attributes work is that they will never exist until a value is set for that attribute for a given persistent object. (eg: Product) So checking to see if it exists via something like: 'badges' in product.custom (which is the recommended way) will often be false even when the custom attribute definition exists because many products have never had a badge value set. Once an object has had a value set to a particular custom attribute even if it is now set to null then it will exist.
Additionally, there are some other issues with the code that may be causing problems. One example is defining the badges variable twice within the same scope. Another example is sni that is placed at the end of the line where you define the products variable. This is likely causing an error in your code. Finally, it is a good practice to avoid the use of the queryAllSiteProducts method if you can. An alternative may be to use the ProductSearchModel instead; This may not always meet your need, but it is a good idea to rule it out before resorting to queryAllSiteProducts for performance reasons.
Something else to consider is that if badges currently has any selected values, you'll be overwriting those values with the code you have today. Consider setting badges initially to [] then check to see if there is a value for that product by doing:
if ('badges' in product.custom && !empty(product.custom.badges) {
badges = product.custom.badges;
}

I'm trying to use jquery to create a div containing columns but I can't get my array to format correctly

I have an array that contains dates. and for some reason I can't get it to show on my screen I've been debugging for a few days now and I've tracked it down to a single line, but the line has worked before and I can't figure out what the issue might be.
The array looks like this:
var selectItems =
[ "05-26-2017", "06-02-2017", "06-09-2017",
"06-16-2017", "06-23-2017", "06-30-2017", "07-07-2017", "07-14-2017",
"07-21-2017", "07-28-2017"...];
It's passed as an argument from another function, but that's how it's showing in console.log().
I might be going about this the wrong way, maybe even a lot further around then I need to but this is what I've come up with:
1. function setTHead(selectItems) {
2 var formatString;
3. for (var x = 0; x < 12; x++) {
4. formatString = selectItems[x].replace(/[^0-9/-]/g, "").toString();
5. console.log(selectItems);
6. $('#datTab').append("<div id='col" + x + "' class='column'>'" + formatString + "'</div>");
7. }
8. }
the array up top is what's showing from the console.log 5 lines down.
the sixth line is what is seeming to give me issues. Nothing is put on the page at all.
I'm getting a console error saying:
jQuery.Deferred exception: selectItems is undefined setTHead#http://localhost/mySite/script.js:136:9
startUp2#http://localhost/mySite/script.js:146:5
#http://localhost/mySite/table.php:19:9
mightThrow#http://localhost/mySite/lib/jquery.js:3586:52
resolve/</process<#http://localhost/mySite/lib/jquery.js:3654:49
setTimeout handler*resolve/<#http://localhost/mySite/lib/jquery.js:3692:37
fire#http://localhost/mySite/lib/jquery.js:3320:30
fireWith#http://localhost/mySite/lib/jquery.js:3450:29
fire#http://localhost/mySite/lib/jquery.js:3458:21
fire#http://localhost/mySite/lib/jquery.js:3320:30
fireWith#http://localhost/mySite/lib/jquery.js:3450:29
ready#http://localhost/mySite/lib/jquery.js:3923:13
completed#http://localhost/mySite/lib/jquery.js:3933:9
EventListener.handleEvent*#http://localhost/mySite/lib/jquery.js:3949:9
#http://localhost/mySite/lib/jquery.js:39:9
#http://localhost/mySite/lib/jquery.js:17:3
undefined
followed by:
TypeError: selectItems is undefined
and thats pointing to line 6.
if anyone has any advice I would be very much appreciative. Thank you in advance.
EDIT: A little more code:
function startTblView(defSel) {
if (defSel === true) {
setCookie('defSel', true, 7);
} else{
setCookie('defSel', false, 7);
}
saveSelected();
window.open('table.php', '_self');
defSel = getCookie('defSel');
if (defSel) {
selectItems = getDefDates();
}else {
selectItems = reGetSelected();
}
setTHead(selectItems);
}
defSel, is a boolean passed from my last page stating whether I'm doing a default view or a custom view, the custom view is passed from saveSelected();
saveSelected is a function for just saving the selected global value as a cookie so I can pull it out on the next page.
getDefDates pulls the default values for the array
reGetSelected, gets the selected array from the cookie.
I apologize for wonky naming conventions. I'm the only one working on this site and I'm just making sure the names don't overlap.
You can do this :
HTML code
<div id="datTab"></div>
JS code
var selectItems =
[ "05-26-2017", "06-02-2017", "06-09-2017",
"06-16-2017", "06-23-2017", "06-30-2017", "07-07-2017", "07-14-2017",
"07-21-2017", "07-28-2017"];
function setTHead(selectItems) {
var formatString;
$.each( selectItems, function( index, value ){
formatString = value.replace(/[^0-9/-]/g, "").toString();
$('#datTab').append("<div id='col" + index + "' class='column'>'" + value + "'</div>");
});
};
You can use $.each, its better than 'for' with javascript.
The .each() method is designed to make DOM looping constructs concise
and less error-prone. When called it iterates over the DOM elements
that are part of the jQuery object. Each time the callback runs, it is
passed the current loop iteration, beginning from 0. More importantly,
the callback is fired in the context of the current DOM element, so
the keyword this refers to the element.
I did a JsFiddle
Here.

How to filter out usercreated events in fullcalendar

I have a fullcalendar where I display non-editable events which are collected from a google calendar, or from a database. Then I want to register customer requests for events from the calendar. This works, but I am not able to list only the events that are added by the user.
Any hint on how to do this?
I tried this:
function retrieve_events() {
var rdv=$('#calendar').fullCalendar( 'clientEvents', undefined);
for (i=0; i<=rdv.length-1; i++) {
/*alert(rdv.toSource());*/
alert(rdv[i].title+" id: "+rdv[i].id+" start: "+rdv[i].start+" end:"+rdv[i].end+" heldag:"+rdv[i].allDay);
}
}
The the "undefined" as id, means that I have given all the non-editable events an id, while the new ones haven't got one. But this way I get all events listed, even those without an id. The same happens with null and ''. But using hardcoded id-numbers returns that specific event.
I see from the documentation that there seems to be other ways to get hold of the events I need, by using other criteria like classes. However I cannot figure out how to specify this filter.
I haven't worked with FullCalendar yet nor do I intend to extensively test this, so I cannot guarantee that this will work.
However, why don't you simple test whether rdv[i].id evaluates to false?
Try:
function retrieve_events( ) {
var rdv = $('#calendar').fullCalendar('clientEvents'),
results = [];
for( var i = 0; i < rdv.length; ++i ) {
if( !rdv[i].id ) {
results.push(rdv[i]);
}
}
return results;
}
P.S.: Passing undefined to .fullCalendar() probably is redundant. It would be equivalent to passing only a single variable. I'd guess the second parameter is a type of events that you can filter for, but passing only a single parameter would cause the plugin to return all events. Also, note that !!'' === false.
The internal check whether the second parameter is set is probably similar to this:
$.fn.fullCalendar = function( command ) {
switch( command ) {
// ... some case's
case 'clientEvents':
var filter = arguments[1];
if( !filter ) {
// Retrieve ALL client events
}
else {
// Filter client events
}
break;
// ... some more case's
}
};
This does not compare types. Testing filter === false would only return true, if filter would evaluate to false and is a boolean.
Following are examples of values that evaluate to false. There may be more, but I believe those are all.
undefined
null
0
false
''

Using one funnction instead of many functions

I have a lot of images that are individually updating a drop down selection box. I've been using the following: (changing the replaceContent(number) and a different selectIndex = (number) inside a separate function for each image).
function replaceContent9() {
document.getElementById("ecwid-productoption-8840317-Backgrounds")
.selectedIndex = 0 ; }
I call the function like this:
javascript:replaceContent9
How would I do this with an array so that I might have just one function that is used for all the images. So far not so good at figuring out arrays. Maybe someone could point me in the right direction or suggest a code to try.
I'd go with a hash/lookup object:
var replaceContent = (function() {
var info = {
0: 'ecwid-productoption-8840317-Backgrounds',
1: 'foobar-product',
9: 'whhooott rage'
};
return function( which ) {
document.getElementById( info[ which ] ).selectedIndex = 0 ;
};
}());
This object in its current form pretty much looks like a javascript array because of indexed key names, but for a more general use you should do it like this.
Now you can just call that function like
replaceContent( 9 );

Categories

Resources