I am using javascript map to store keys and values. Later on I check if specified key is present in map or not, but it sometimes gives correct result but sometimes it don't. I tried to print the map using console.log(mapname), it shows all keys, but if I try to check if some specified key is present - sometimes it gives wrong answer.
Am using following code:
// following code is called n times in loop with different/same vales of x
myMap : new Object();
var key = x ; // populated dynamically actually
myMap[key] = "dummyval";
if(myIdMap[document.getElementById("abc").value.trim()] != null)
alert('present');
else
alert('not present');
What can be the possible problem? Can alphanumericstring/integers values can be used as keys?
var myMap = {};
// ...
//simulating your inner loop; is this close enough?
//Note: A MacGuffin is a plot device to move the story forward.
// Like a time machine, or a space ship.
for (var key in macGuffin) {
myMap[key] = macGuffin.processItem(key);
}
// ...
var myKey = document.getElementById("abc").value.trim();
//Note the use of !==; false == null == 0 == '', but null only === null
if (myMap[myKey]!==null)
//Better? (typeof myMap[myKey] != 'undefined')
console.log('present');
else
console.log('not present');
Be sure you're aware that the value stored with the key could be null, as the following code shows:
a = {
"mykey": null
}
for (x in a) {
if (a[x] == null) {
alert(x + " is null!");
}
} // produces a single alert: "mykey is null!"
I think you have to store keys in some temporary table or objects like session or cookies for future retrieve.
you have dynamically store x position value in mymap[key] but every time it loads and overwrite your new values. you something doing wrong there..
Related
I trying to make a function that tries to access the first item in a list to make a konami code website. The list is defined and then the function is defined to edit the list. This is code
<html><body>
<script>
var keys = [38,38,40,40,37,39,37,39,66,65];
function keyhandler (e) {
e = e || event;
console.log(e.keyCode);
var key = keys[0];
if (key == e.keyCode) {
console.log("correct key");
if (keys.length == 1){
console.log("konami");
} else {
var keys = keys[1,keys.length];
}
}
else {
var keys = [38,38,40,40,37,39,37,39,66,65];
}
}
document.onkeydown = keyhandler;
</script>
</body></html>
when I trigger the function by a key-press I get this error :
Uncaught TypeError: Cannot read property '0' of undefined the error is caused by the var key = keys[0] line
You're initializing another variable named keys inside of your function (var keys = keys[1, keys.length]). This shadows the outer variable. Remove the var keyword to access the correct variable.
Also, I believe you want to remove the first element from the list. To do so, use shift.
keys.shift();
This will modify the array directly so you don't need to assign the result to anything.
var keys = keys[1,keys.length];
You've created a new (locally scoped) keys variable inside your function and it is masking the one containing the array you are trying to read. Remember: var statements are hoisted.
Use different names for those two variables (or if they are supposed to be the same variable, remove the var statement from the inner one).
NB: In JavaScript, [1,keys.length] is just a comma operator it isn't a slice.
As Quentin said, hoisting is happening in your function.
Other posters here also said it, you are defining a new local variable in your function.
Let me just show your function with hoisting happening when Javascript interprets your code:
var keys = [38,38,40,40,37,39,37,39,66,65];
function keyhandler (e) {
// Javascript defines all the local variables at top.
// This is what hoisting is
var key;
var keys;
e = e || event;
console.log(e.keyCode);
// at this point, local variable keys isn't initialized,
// that's why you are getting the undefined error.
key = keys[0];
if (key == e.keyCode) {
console.log("correct key");
if (keys.length == 1){
console.log("konami");
} else {
// you are referencing a local variable here
keys = keys[1,keys.length];
}
}
else {
// you are referencing a local variable here
keys = [38,38,40,40,37,39,37,39,66,65];
}
}
document.onkeydown = keyhandler;
As other posters mention, simply removing the var from keys may fix your issue, if that's what intended, but I think it's important to understand what Javascript technically does in this situation.
the issue was that
var key = keys[0];
is not a javascript expression as mentioned in the comment it needed to be replaced with
var key = keys.slice(0,2);
Edit: I have figured out the problem. There were several bugs in my originally posted code that came from my (very) poor understanding of COM, most notably not understanding what the calls to GetDispID were doing. I'll update the post with the correctly working code, in case anyone else runs into this issue.
I have a C++ application (MFC) with an embedded web browser. We are calling C++ functions from javascript using:
var arrResult = window.external.MyFunction (myArgs);
It works well. We send data back as an array using the code from CodeProject article http://www.codeproject.com/Articles/88753/Creating-JavaScript-arrays-and-other-objects-from
Now we need to send back a 2-dimensional array. This code works for me, now. Note that, in javascript, my 2 dimensional array really comes as an Object, not an array. So, isArray returns false:
oMyResult = window.external.VPQuery (strQuery);
if (Array.isArray (oMyResult))
alert ("This is an array!");
But this will be successful:
if( (typeof arrVPResult === "object") && (arrVPResult !== null) )
alert ("This is an object!");
So I can iterate through it like this:
for (var strProp in arrVPResult) {
if (arrVPResult.hasOwnProperty (strProp)) {
// do something...
}
}
Edit #2: If the call to theObject->GetDispID uses a numeric value rather than a text value (where CComBSTR(rarrData[n]) is way down below) then the created object will all act like an array in javascript (and have a .length value). If you use a text value, it will act like an object (and will not have a .length value).
Anyway, here's my C++ code. I hope it's useful for someone.
bool CMyHtmlDispatch::BuildJsArrayTest (CStringArray& rarrData, VARIANT& vtResult)
{
CComQIPtr<IDispatchEx> theObject;
DISPID didMainArray = 0;
DISPID didCurSubArray = 0;
DISPID didTemp = 0;
//
// First create our main array. The elements in this array will be
// arrays, too, giving us our 2 dimensional array. We'll operate on
// this array using theObject. As far as I can tell, we don't need didMainArray (the dispatch ID)
if (! BuildJsArray_CreateObject (vtResult, theObject, didMainArray))
return false;
if (NULL == theObject)
return false;
//
// Now create our sub-arrays. Each of them will contain an array of strings.
CString strIndex;
for(size_t n = 0; n < rarrData.GetCount(); n++)
{
CComQIPtr<IDispatchEx> theObjectSubArray;
_variant_t vtSubArray;
//
// Just like above, this creates our javascript array object. We'll refer to it
// using theObjectSubArray. We won't need to use didCurSubArray (the dispatch ID)
if (! BuildJsArray_CreateObject (vtSubArray, theObjectSubArray, didCurSubArray))
return false;
//
// Add our data to the sub-array, then we'll add the sub-array to the main array
// In our case, we'll just create some test data. We'll use the value in rarrData
// as the Key name and fill the sub-array values with variations on the key name.
for (int x = 0; x < 3; x ++) {
//
// First create the key for our index in our sub array. We'll just use the index
// number as the key, though we have to pass it to GetDispID as a string.
// fdexNameEnsure makes theObjectSubArray create it for us. It sends the dispatch ID
// back that we'll use below to set the value for our key in InvokeEx
strIndex.Format (_T("%d"), x);
if (FAILED (theObjectSubArray->GetDispID (CComBSTR(strIndex), fdexNameEnsure, &didTemp)))
break;
//
// Now add our sub-item value string to the sub-array object. We'll call
// InvokeEx on theSubObjectArray and need to pass in didTemp so it knows which
// key we're setting the value for.
CString s; s.Format (_T("%s - %d"), rarrData[n], x);
CComVariant vArg (s);
DISPID namedArgs[] = {DISPID_PROPERTYPUT};
DISPPARAMS p = {&vArg, namedArgs, 1, 1};
if (FAILED (theObjectSubArray->InvokeEx(didTemp, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &p, NULL, NULL, NULL)))
return false;
}
//
// OK! Now, in theory, all the data for rarrData[n] is in didCurSubArray. Let's
// add that to the main array. Remember that GetDispID returns a dispatch ID that
// we'll use in our call to InvokeEx.
if (FAILED (theObject->GetDispID (CComBSTR(rarrData[n]), fdexNameEnsure, &didTemp)))
break;
CComVariant vArg (vtSubArray);
DISPID namedArgs[] = {DISPID_PROPERTYPUT};
DISPPARAMS p = {&vArg, namedArgs, 1, 1};
//
// Because our vArg really contains an IDispatch, we have to use DISPATCH_PROPERTYPUTREF
// rather than DISPATCH_PROPERTYPUT. So here we're associating that sub array with the
// key whose dispatch ID is didTemp.
if (FAILED (theObject->InvokeEx (didTemp, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUTREF, &p, NULL, NULL, NULL))) // DISPATCH_PROPERTYPUT
return false;
} // end loop through main data
return true;
} // end build js array int
//////////////////////////////////////////////////////////////////////////////
//
// This does the grunt work
//
// This function turns our MFC array into a native Javascript array.
// Javascript can't handle SAFEARRAYS directly. It _can_ handle them
// by using the javascript object VBArray, like this:
// var arrResult = new VBArray(window.external.CB_CustomFunctionWithParams (oRed, nPi)).toArray()
//
// But by doing the BuildJsArray stuff it lets the javascript side
// treat it just like a native javascript array.
//
// I got this code from:
// http://www.codeproject.com/Articles/88753/Creating-JavaScript-arrays-and-other-objects-from
//
bool CMyHtmlDispatch::BuildJsArray_CreateObject (VARIANT& vtResult, CComQIPtr<IDispatchEx>& rpObject, DISPID& did)
{
if (NULL == m_pHtmlView)
return false;
CWebDocument2Ptr pWebDocument2 = m_pHtmlView->GetWebDocument2 ();
if (NULL == pWebDocument2)
return false;
CComQIPtr<IDispatch> scriptDispatch;
pWebDocument2->get_Script (&scriptDispatch);
if (!scriptDispatch)
return false;
//
// get DISPID for "Array"
did = 0;
LPOLESTR lpNames[] = {L"Array"};
HRESULT hr = scriptDispatch->GetIDsOfNames(IID_NULL, lpNames, 1, LOCALE_USER_DEFAULT, &did);
if (FAILED(hr))
return false;
//
// invoke scriptdispatch with DISPATCH_PROPERTYGET for "Array"
CComVariant vtRet;
DISPPARAMS params = {0};
hr = scriptDispatch->Invoke(did, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &vtResult, NULL, NULL);
if (FAILED(hr))
return false;
//
// check result: should be a VT_DISPATCH
if ((VT_DISPATCH != vtResult.vt) || (NULL == vtResult.pdispVal))
return false;
//
// get IDispatchEx on returned IDispatch
CComQIPtr<IDispatchEx> prototype(vtResult.pdispVal);
if (!prototype)
return false;
//
// call InvokeEx with DISPID_VALUE
// and DISPATCH_CONSTRUCT to construct new array
VariantClear (&vtResult);
hr = prototype->InvokeEx(DISPID_VALUE, LOCALE_USER_DEFAULT, DISPATCH_CONSTRUCT, ¶ms, &vtResult, NULL, NULL);
if (FAILED(hr))
return false;
//
// vtresult should contain the new array now.
if ((VT_DISPATCH != vtResult.vt) || (NULL == vtResult.pdispVal))
return false;
//
// get IDispatchEx on returned IDispatch
CComQIPtr<IDispatchEx> theObject(vtResult.pdispVal);
if (!theObject)
return false;
rpObject = theObject;
return true;
}
I have a list of objects as shown in the image.
These all have the property statusCode: 62467 but the journey property goes like: 0,1,2,3,3,4,4,4,4
I want to loop through these objects and return the FIRST of the duplicated (they are not the same object, just that both have the same journey number and the same status code) objects with the same journey number.
So I want to return the bold objects: 0,1,2,3,3,4,4,4,4
$.each(points, function (index, point) {
for (i = 0; i < journeyNumber.length; i++) {
if (point.k.journey === journeyNumber[i] && point.k.statusCode === '62467') {
console.log(point);
latlngs.push(point.j.aa.k);
latlngs.push(point.j.aa.B);
}
}
});
The screenshot is the log of console.log(point), so ideally I would like another loop inside which returns only the first object of the same journey number.
Hope this makes sense and thank you for your time.
Try this,
var temp = [];
$.each(points, function (index, point) {
if (temp.indexOf(point.k.journey) === -1) {
temp.push(point.k.journey);
console.log(point);
latlngs.push(point.j.aa.k);
latlngs.push(point.j.aa.B);
}
});
Create a fresh object with status codes and check against that.
var journeys = {};
for(object in points){
// extract the properties you want (or use them directly, this is not necessary)
var journey = points[object].journey;
var status = points[object].statusCode;
// use the typeof operator to see if the journey has already been set before
if(typeof journeys[journey] == "undefined"){
// then define it.
journeys[journey] = status;
}
}
(Please note I am not actually correctly referencing the journey and statusCode, you'd have to do something like objects[object][k].journey to access the right property, but thats not really the point)
You can even add anything you want into the journeys object, nesting another object with the extracted latitude and longitude, or even just nesting the entire object in the journey!
journeys[journey] = points[object];
Now you can get every journey by looping through them again, and the associated first statusCode:
for(journey in journeys){
console.log("First instance of journey " + journey + " had statusCode " + journeys[journey]);
}
var status = result.locations[index].status;
var operator = result.locations[index].operator;
var original = result.locations[index].original;
var produced = result.locations[index].produced;
var href = result.locations[index].more;
I have the above which each need to be an if statement to check if there is content and my output is the below code.
if (result.locations[index] && result.locations[index].status){
var status = result.locations[index].status;
} else {
var status = '';
}
I would need to reproduce this per line from the code at the top of the post. What would be the best method to simplify each down to keep the code neater and not produce 5 lines of if statement when 1 or 2 would do.
var status = (result.locations[index] && result.locations[index].status ? result.locations[index].status : '');
Not sure why you want to, but:
var status = (result.locations[index] && result.locations[index].status) ? result.locations[index].status : ""
Your problem is trying to access a property of a "deep" javascript object using its path.
This is a common question :
Javascript: Get deep value from object by passing path to it as string
Accessing nested JavaScript objects with string key
There is no built-in way to do this in javascript.
There are plenty of libraries to do that, for example, with selectn, this would become something like (I have not tested it, so I don't know if the index part will work, but you get the idea) :
var status = selectn("locations." + index + ".status", result) || ''
If the structure of your objects is always the one above (that is, the property is just at one level of depth), and you're not expecting 'falsy', you could simply write the 'test' function yourself :
function safeGet(instance, propertyName, defaultValue) {
// As pointed by AlexK, this will not work
// if instance[propertyName] can be anything Falsy ("", 0, etc...)
// If it's possible, get a library that will do
// the full series of insane checks for you ;)
if (instance && instance[propertyName)) {
return instance[propertyName];
} else {
return defaultValue;
}
}
var location = result.locations[index]; // Potentially undefined, but safeGet will deal with it
var status = safeGet(location, "status", "");
var operator = safeGet(location, "operator", "DEFAULT_OPERATOR");
...
var status = result.locations[index] && result.locations[index].status || '';
However, better maje sure before, if result.locations[index] exists... else do whatever is to be done in your code..
So I have this javascript function, reOrderJSON, and it orders an array, and, if the argument, order equates to "reverse", it reverse the array right after it has been sorted.
I also have a function bound to the click event of an associated link.
function reOrderJSON(subj,att,order,limit){
subj.sort(function (a, b,order) {
a = a[att],
b = b[att];
return a.localeCompare(b);
});
if(order === "reverse"){
subj.reverse();
console.log("Reverse passed a parameter to reOrderJSON")
}
layoutDate(subj,limit)
return depos = subj;
}
function clickReorder(e){
var orderingLink = e.target;
var reverseOrder = "";
var order = $(orderingLink).attr('data-order');
if(order === "desc" || order === "undefined"){
$(orderingLink).attr('data-order','asc');
console.log("Order detected as "+order)
reverseOrder = false;
}else{
$(orderingLink).attr('data-order','desc');
console.log("Order detected as "+order)
reverseOrder = "reverse";
}
var limit = $('.entries-per-page').val();
reOrderJSON(e.data.orderedObject,$(e.target).data('assoc'),reverseOrder,limit);
$('.result th').css('background','transparent');
$(orderingLink).closest('th').css({'background':'pink'});
return false;
}
$('.result').on("click", "th a", { orderedObject: depos }, clickReorder);
The link looks like this at the start:
<th>County</th>
As you can tell, I'm logging some debug text to tell what's going on. The first time I click a link, I always get these two lines returned in the log:
Order detected as undefined
Reverse passed a parameter to reOrderJSON
I don't understand how this is happening, order should not be passed as "reverse" the first time. What am I missing here?
depos is an array object, if that makes any difference.
Your comparison
order === "undefined"
is currently checking for the string "undefined", but you actually want to be checking for an undefined value in a variable.
You really want:
order == undefined // no quotes -- it's not a string
Or, even better:
typeof order == "undefined"
since undefined can be overwritten in non-strict mode.