I’m wondering how to solve a matching/lookup problem and I “think” a multi-dimensional array is the solution. In short, I want to match a list of comma separated SKUs stored as a cookie value against a finite list of SKUs with matching product names and print out the matched product names onto the page. I’m not sure if this is the best way to do this, but with what I have so far I’m not clear how to properly breakup the comma separated strings from the cookie (right now it’s trying to match the entire cookie value), match them to the matrix (17 total rows) and then print out the Product Name.
<script>
var staticList = [
[“1234”, “Chocolate Ice Cream”],
[“1235”, “Peanut Butter Cookie”],
[“6G2Y”, “Raspberry Jell-O”],
[“YY23”, “Vanilla Wafers”]
];
var cookieSkus = [‘1235,YY23’]; // comma separated value from cookie
jQuery(function () {
for (var i = 0; i < staticList.length; i++) {
if (cookieSkus.indexOf(staticList [i][0]) > -1) {
jQuery('#pdisplay).append(staticList [i] [1] + '<br />');
}
}
});
</script>
<p id=”pdisplay”></p>
In this example, the paragraph "pdisplay" would contain:
Peanut Butter Cookie
Vanilla Wafers
Is there a way to correct what I have above or is there a better method of accomplishing what I’m trying to do?
First, you might want to focus on the Cookie SKUs rather than the staticList. The reason for this is that the cookie may have a variable number, and may be as small as 0 elements. (After all, we don't need to list the items if there are no items).
This may be accomplished simply by converting the string to an array and then checking if the SKU is in the staticList. Unfortunately, since you are using a multidimensional array, this would require going through the staticList for each cookie sku. Using just this suggestion, here is a basic example and fiddle:
Rewrite: Accounting for the fact that staticList is an Array of Arrays
jQuery(function() {
var skus = cookieSkus[0].split(',');
for (var i = 0; i < skus.length; i++) {
for (var j = 0; j < staticList.length; j++) {
if (staticList[j][0] == skus[i]) {
jQuery('#pdisplay').append(staticList[j][2] + '<br/>');
break; // Will end inner if the item is found... Saves a lot of extra time.
}
}
}
});
Edit 2: Using an Object (A possibly better approach)
According to the comments, you must support IE8. In this case, you might consider an Object instead of a multi-dimensional array. The reasons for this are as follows:
An object is actually an associative array (with a few perks).
You can directly check for property existence without having any nested arrays.
Object property access is typically faster than looping through an array
You can access object properties nearly exactly like accessing an array's elements.
When using an Object, the original version of my code may be used without modification. This is because the object's structure is simpler. Here is a fiddle for you: option 2
var staticList = {
"1234": "Chocolate Ice Cream",
"1235": "Peanut Butter Cookie",
"6G2Y": "Raspberry Jell-O",
"YY23": "Vanilla Wafers"
};
jQuery(function() {
var skus = cookieSkus[0].split(',');
for (var i = 0; i < skus.length; i++) {
if (staticList[skus[i]])
jQuery('#pdisplay').append(staticList[skus[i]] + '<br/>');
}
});
Responding to your comment:
The reason that the output matches what is desired is because unlike an array which has numerical indices, the object's indices are the actual skus. So, there is no staticList[0] if staticList is an object. Instead (in the context of the staticList object), 1234 = "Chocolate Ice Cream". So, an object definition basically goes as follows:
var objectName = {
index1: value1,
index2: value2,
...,
...
}
The index may be any primitive value (integer or string). The value may be any valid javascript value including a function or an inner object. Now, to get the value at a specific index, you may do either:
objectName.index1 (no quotes)
OR:
objectName["index1"] (quotes needed if the index is a string)
The result of either of those will be:
value1
It's as simple as that.
I would try something like this:
var cookieSkus = cookieSkus[0].split(',');
staticList.filter(function(cell){
return cookieSkus.some(function(val){return cell[0] === val; });
}).map(function(cell){
jQuery('#pdisplay).append(cell[1] + '<br />');
});
Disclaimer: provided based on the sample code provided above along with recent comments
Related
I know there are a lot of questions about this, but I can't find the solution to my problem and have been on it for a while now. I have two sets of input fields with the same name, one for product codes, and one for product names. These input fields can be taken away and added to the DOM by the user so there can be multiple:
Here is what I have so far, although this saves it so there all the codes are in one array, and all the names in another:
var updatedContent = [];
var varCode = {};
var varName = {};
$('.productVariationWrap.edit input[name="varVariationCode[]"]')
.each(function(i, vali){
varCode[i] = $(this).val();
});
$('.productVariationWrap.edit input[name="varVariationName[]"]')
.each(function(i1, vali1){
varName[i1] = $(this).val();
});
updatedContent.push(varCode);
updatedContent.push(varName);
I am trying to get it so the name and code go into the same array. i.e. the code is the key of the K = V pair?
Basically so I can loop through a final array and have the code and associated name easily accessible.
I can do this in PHP quite easily but no luck in javascript.
EDIT
I want the array to look like:
[
[code1, name1],
[code2, name2],
[code3, name3]
];
So after I can do a loop and for each of the arrays inside the master array, I can do something with the key (code1 for example) and the associated value (name1 for example). Does this make sense? Its kind of like a multi-dimensional array, although some people may argue against the validity of that statement when it comes to Javascript.
I think it's better for you to create an object that way you can access the key/value pairs later without having to loop if you don't want to:
var $codes = $('.productVariationWrap.edit input[name="varVariationCode[]"]'),
$names = $('.productVariationWrap.edit input[name="varVariationName[]"]'),
updatedContent = {};
for (var i = 0, il = $codes.length; i < il; i++) {
updatedContent[$codes.get(i).value] = $names.get(i).value;
}
Now for example, updatedContent.code1 == name1, and you can loop through the object if you want:
for (var k in updatedContent) {
// k == code
// updatedContent[k] == name
}
Using two loops is probably not optimal. It would be better to use a single loop that collected all the items, whether code or name, and then assembled them together.
Another issue: your selectors look a little funny to me. You said that there can be multiple of these controls in the page, but it is not correct for controls to have duplicate names unless they are mutually exclusive radio buttons/checkboxes--unless each pair of inputs is inside its own ancestor <form>? More detail on this would help me provide a better answer.
And a note: in your code you instantiated the varCode and varName variables as objects {}, but then use them like arrays []. Is that what you intended? When I first answered you, i was distracted by the "final output should look like this array" and missed that you wanted key = value pairs in an object. If you really meant what you said about the final result being nested arrays, then, the smallest modification you could make to your code to make it work as is would look like this:
var updatedContent = [];
$('.productVariationWrap.edit input[name="varVariationCode[]"]')
.each(function(i, vali){
updatedContent[i] = [$(this).val()]; //make it an array
});
$('.productVariationWrap.edit input[name="varVariationName[]"]')
.each(function(i1, vali1){
updatedContent[i1].push($(this).val()); //push 2nd value into the array
});
But since you wanted your Code to be unique indexes into the Name values, then we need to use an object instead of an array, with the Code the key the the Name the value:
var updatedContent = {},
w = $('.productVariationWrap.edit'),
codes = w.find('input[name="varVariationCode[]"]'),
names = w.find('input[name="varVariationName[]"]');
for (var i = codes.length - 1; i >= 0; i -= 1) {
updatedContent[codes.get(i).val()] = names.get(i).val();
});
And please note that this will produce an object, and the notation will look like this:
{
'code1': 'name1',
'code2': 'name2',
'code3': 'name3'
};
Now you can use the updatedContent object like so:
var code;
for (code in updatedContent) {
console.log(code, updatedContent[code]); //each code and name pair
}
Last of all, it seems a little brittle to rely on the Code and Name inputs to be returned in the separate jQuery objects in the same order. Some way to be sure you are correlating the right Code with the right Name seems important to me--even if the way you're doing it now works correctly, who's to say a future revision to the page layout wouldn't break something? I simply prefer explicit correlation instead of relying on page element order, but you may not feel the need for such surety.
I don't like the way to solve it with two loops
var updatedContent = []
$('.productVariationWrap.edit').each(function(i, vali){
var $this = $(this)
, tuple = [$this.find('input[name="varVariationCode[]"]').val()
, $this.find('input[name="varVariationName[]"]').val()]
updatedContent.push(tuple)
});
I have a string that is JSON values separated by /r. It's sort of like records in a DB table. It looks like:
"{"id":"id","hole":"hole","stat":"stat","value":"value"}/r{"id":1354075540949,"hole":"1","stat":"score","value":"4"}/r{"id":1354075540949,"hole":"1","stat":"putts","value":"1"}/r{"id":1354075540949,"hole":"1","stat":"fir","value":"y"}/r{"id":1354075540949,"hole":"1","stat":"gir","value":"n"}/r"
The first row is the column names (id, hole, stat, value) and I just give them the same value. All other rows separated by /r is the actual data.
I split this string by /r, then loop through the result and push the result of JSON.parse() of each element to an array so now I have an array of objects with properties of the given structure (id, hole, stat, value). Everything is working except the 'id' field ends up being true or false instead of the big long number. Why is it doing that?
var tblData = localStorage.getItem(tblName).split("/r");
var data = new Array();
// fill the array
for (i = 1; i < tblData.length - 1; i++)
data.push(JSON.parse(tblData[i]));
[EDIT]
Seems this does work, but there is a jQuery.grep() I run right after this that's setting the id properties to true/false.
var changeRecords = jQuery.grep(data, func);
Where func is:
function (v) { return v.id == gCurrentRoundID && v.hole == gCurrentHole; }
Not sure why it would be setting id to true/false though.
[EDIT2]
Nevermind, I found my error. The function above wasn't the right one and the one I did have only had 1 equal sign for v.id = gCurrentRoundID, which is why it was setting it to true/false.
I would just manually change the whole string to valid JSON. Have it start with a [ and end with a ], then replace all those /rs with commas. The end result should look like
"[{"id":"id","hole":"hole","stat":"stat","value":"value"},{"id":1354075540949,"hole":"1","stat":"score","value":"4"},{"id":1354075540949,"hole":"1","stat":"putts","value":"1"},{"id":1354075540949,"hole":"1","stat":"fir","value":"y"},{"id":1354075540949,"hole":"1","stat":"gir","value":"n"},]"
Then parse that through JSON.parse
Just note that that last trailing comma may cause problems in IE8. If so, you should be able to manually fix that fairly easily. Something like s = s.substr(0, s.length - 2) + ']';
I have a for loop that cycles through the number of elements that the user has created. There are a lot of available settings in this plugin, and each element can receive it's specific settings.
User settings are entered in the following format: speed_x: "1000,500 > 1000,200 > 0,0"
This controls the speed_x in/out for 3 separate elements. The > divides by object and the commas separate the in/out.
So I can grab specific object speed_x values, I've split speed_x into speed_x_set (splitting by >) resulting in:
1 1000,500
2 1000,200
3 0,0`
3 Inside the loop, I grab the value by index (since it's the object #) and split it by comma (to get speed_x_in and speed_x_out.)
for(var i=0; i<OS.numberofobjects; ++i){
OS.speed_x_on_set[i]=speed_x_set[i].split(",")[0],
OS.speed_x_off_set[i]=speed_x_set[i].split(",")[1],
...
};
Everything is assigned by object and by setting in/out correctly into the master OS settings object. T*he problem is I have many, many settings which need to be split in this fashion...* for example: delay_x_set, speed_y_set, opacity_set, etc. Their names are all based on the default setting name, with "_set" added as shown above. Hopefully this provides enough information. Thanks!
I would avoid to access to the same item twice and perform the same split twice for each iteration. So, you could have something like:
for (var i = 0, item; item = speed_x_set[i++];) {
var values = item.split(",");
OS.speed_x_on_set.push(values[0]);
OS.speed_x_off_set.push(values[1]);
}
Notice that in JavaScript 1.7 (Firefox) you can simply have:
for (var i = 0, item; item = speed_x_set[i++];) {
var [on, off] = item.split(",");
OS.speed_x_on_set.push(on);
OS.speed_x_off_set.push(off);
}
And hopefully in the next version of ECMAScript as well.
It's called "destructuring assignment".
I would say to cache the split result
for(var objindex=0; objindex<OS.numberofobjects; ++objindex){
var splits = speed_x_set[objindex].split(","); //Cache the split so its does not need to be done twice
OS.speed_x_on_set[objindex] = splits[0];
OS.speed_x_off_set[objindex] = splits[1];
...
};
What you're looking for is called parallel assignment, but unfortunately, JavaScript doesn't have it.
In ruby, however, it is common to see similar patterns:
first, second = "first second".split
As others have noted, the obvious way would be to cache split results and assign them separately. Sorry for not answering your question directly.
i am trying to validate if a certain company was already picked for an application. the companyList format is:
60,261,420 ( a list of companyID)
I used
cID = $('#coName').val().split('::')[1];
to get the id only.
I am calling this function by passing say 60:
findCompany = function(value) {
var v = /^.+60,261,420$/.test(value);
alert(v);
}
when I pass the exact same string, i get false. any help?
Well if your company list is a list of numeric IDs like that, you need to make the resulting regular expression actually be the correct expression — if that's even the way you want to do it.
Another option is to just make an array, and then test for the value being in the array.
As a regex, though, what you could do is this:
var companyList = [<cfoutput> whatever </cfoutput>]; // get company ID list as an array of numbers
var companyRegex = new RegExp("^(?:" + companyList.join('|') + ")$");
Then you can say:
function findCompany(id) {
if (companyRegex.test(id)) alert(id + " is already in the list!");
}
Why not split the string into an array, like you did for your testing, iterate over the list and check if it's in?
A regexp just for that is balls, overhead and slower. A lot.
Anyway, for your specific question:
You’re checking the string "60" for /^.+60,261,420$/.
.+60 will obviously not match because you require at least one character before the 60. The commas also evaluate and are not in your String.
I don’t quite get where your regexp comes from.
Were you looking for a regexp to OR them a hard-coded list of IDs?
Code for splitting it and checking the array of IDs:
findCompany = function(value) {
$('#coName').val().split('::').each(function(val){
if(val == value) return true;
});
return false;
}
I have a group of strings in Javascript and I need to write a function that detects if another specific string belongs to this group or not.
What is the fastest way to achieve this? Is it alright to put the group of values into an array, and then write a function that searches through the array?
I think if I keep the values sorted and do a binary search, it should work fast enough. Or is there some other smart way of doing this, which can work faster?
Use a hash table, and do this:
// Initialise the set
mySet = {};
// Add to the set
mySet["some string value"] = true;
...
// Test if a value is in the set:
if (testValue in mySet) {
alert(testValue + " is in the set");
} else {
alert(testValue + " is not in the set");
}
You can use an object like so:
// prepare a mock-up object
setOfValues = {};
for (var i = 0; i < 100; i++)
setOfValues["example value " + i] = true;
// check for existence
if (setOfValues["example value 99"]); // true
if (setOfValues["example value 101"]); // undefined, essentially: false
This takes advantage of the fact that objects are implemented as associative arrays. How fast that is depends on your data and the JavaScript engine implementation, but you can do some performance testing easily to compare against other variants of doing it.
If a value can occur more than once in your set and the "how often" is important to you, you can also use an incrementing number in place of the boolean I used for my example.
A comment to the above mentioned hash solutions.
Actually the {} creates an object (also mentioned above) which can lead to some side-effects.
One of them is that your "hash" is already pre-populated with the default object methods.
So "toString" in setOfValues will be true (at least in Firefox).
You can prepend another character e.g. "." to your strings to work around this problem or use the Hash object provided by the "prototype" library.
Stumbled across this and realized the answers are out of date. In this day and age, you should not be implementing sets using hashtables except in corner cases. You should use sets.
For example:
> let set = new Set();
> set.add('red')
> set.has('red')
true
> set.delete('red')
true
> set.has('red')
false
Refer to this SO post for more examples and discussion: Ways to create a Set in JavaScript?
A possible way, particularly efficient if the set is immutable, but is still usable with a variable set:
var haystack = "monday tuesday wednesday thursday friday saturday sunday";
var needle = "Friday";
if (haystack.indexOf(needle.toLowerCase()) >= 0) alert("Found!");
Of course, you might need to change the separator depending on the strings you have to put there...
A more robust variant can include bounds to ensure neither "day wed" nor "day" can match positively:
var haystack = "!monday!tuesday!wednesday!thursday!friday!saturday!sunday!";
var needle = "Friday";
if (haystack.indexOf('!' + needle.toLowerCase() + '!') >= 0) alert("Found!");
Might be not needed if the input is sure (eg. out of database, etc.).
I used that in a Greasemonkey script, with the advantage of using the haystack directly out of GM's storage.
Using a hash table might be a quicker option.
Whatever option you go for its definitely worth testing out its performance against the alternatives you consider.
Depends on how much values there are.
If there are a few values (less than 10 to 50), searching through the array may be ok. A hash table might be overkill.
If you have lots of values, a hash table is the best option. It requires less work than sorting the values and doing a binary search.
I know it is an old post. But to detect if a value is in a set of values we can manipulate through array indexOf() which searches and detects the present of the value
var myString="this is my large string set";
var myStr=myString.split(' ');
console.log('myStr contains "my" = '+ (myStr.indexOf('my')>=0));
console.log('myStr contains "your" = '+ (myStr.indexOf('your')>=0));
console.log('integer example : [1, 2, 5, 3] contains 5 = '+ ([1, 2, 5, 3].indexOf(5)>=0));
You can use ES6 includes.
var string = "The quick brown fox jumps over the lazy dog.",
substring = "lazy dog";
console.log(string.includes(substring));