I have an Object:
var myObj = { 5: "foo", 25: "bar" };
Now i want to walk through the object like this:
$.each(myObj, function(key, value) {
console.log(value);
});
The problem is, that jQuery walks 26 times through the array while value will be "undefined" in 24 cases (i debugged it - thats fact). So it walks through the object 5 times before it reaches the fifth value (which is the first one).
I dont want to convert the object to an array since i want to store the data back into the stack after handling the data.
Is this a jQuery-Bug, a bug of mine or how to solve this?
Additional Info:
Here's a screenshot of my Chrome's DevTools shows the object at runtime.
sT.data.tasks contains the same content as splitTicket - just needed to show splitTicket because sT.data.tasks is lost inside the $.each-Scope. (Maybe that indicates the problem?) - Another thing to note is the "length" of the object with key "2517" - Is that a Google-Chrome Bug, a javascript Bug or just correct?
-> Note that at the current breakpoint tkey is 0 (int) and tval is undefined (undefined) and since this code wont produce any Errors, it walks through it 2517 times (did not count^^)
It's not a bug at all. It looks like jQuery is just treating this object as an array and iterating over it in the normal way, starting from 0 and going to the largest key.
A for..in loop loops over the properties in the object.
Using the following only goes through 2 times:
for(key in myObj) {
console.log(myObj[key]);
};
Your example looks just fine to me:
var myObj = { 5: "foo", 25: "bar" };
$.each(myObj, function(key, value) {
console.log(value);
});
foo
bar
Looking at the jQuery source for $.each it appears to use for..in if your object isn't "arraylike".
I'd hypothesise that isArraylike returns true for your 'real' code (it's false for myObj)
jQuery $.each:
for ( i in obj ) {
value = callback.call( obj[ i ], i, obj[ i ] );
if ( value === false ) { break; }
}
jQuery isArraylike:
function isArraylike(obj) {
var length = obj.length,
type = jQuery.type(obj);
if (jQuery.isWindow(obj)) {
return false;
}
if (obj.nodeType === 1 && length) {
return true;
}
return type === "array" || type !== "function" && (length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj);
}
try this,
var myObj = { 5: "foo", 25: "bar" };
var i=0;
Object.keys( myObj ).forEach(function ( name, index ) {
var value = myObj[name];
console.log(++i);
console.log(name); // the property name
console.log(value); // the value of that property
console.log(index); // the counter
});
live DEMO
your code iterate two time at my side.but also you can try using this example.
Related
I've been doing some iterations to obtain a certain amount of values.
$.each(Obj, function(k,v){
$.each(v, function(j,l){
$.each(l, function(r,s){
if(l.hasOwnProperty(r) && typeof s === 'object'){
}
})
})
})
In this code, you'll see that I'm iterating over an object named "Obj". Inside of it we'll have a few amount of arrays and objects, because is a very complex structure. But let's cut the chatter. Once we arrive to this part...
$.each(l, function(r,s){
if(l.hasOwnProperty(r) && typeof s === 'object'){
}
})
You'll see that we have a conditional. In that conditional I'm checking for those properties "r" that have a typeof value of 'object'. So, if I do a console check of "r" inside my conditional, I will actually see those specific properties.
So, here's the thing. Why am I doing this? Well I'm building a variable that will ONLY store those "l" elements that have a child property with 'object' as a typeof value. That's actually working, but the problem is that, for my purposes, I need this variable not only to work as intended, but also to store only the elements that have MORE THAN ONE property with 'object' as a typeof value.
So, let's consider this case:
If I have two objects like the following...
Obj1: [{"key":"value", "key":"value", "key":"[{"key":"value", "key":"value"}, { "key":"value", "key":"value"}]"}]
Obj1: [{"key":"value", "key":"value", "key":"[{"key":"value", "key":"value"}, { "key":"value", "key":"value"}]", "key":"[{"key":"value", "key":"value"}, { "key":"value", "key":"value"}]"}]
I want my variable to store ONLY the second one, because it has 2 values with objects inside of it.
So, In my conditional I would like to use some kind of logic that let me know not only if my "l" element has an object inside of it, but also, how many of them do I have, and if they're lesser than one, then my console log shouldn't bring them up.
Hope it's clear enough.
My idea is to design something that would be recursive and would not blow the stack:
function inspect (obj,f,e,ledger) {
/*obj is the object to inspect,
f is the call back,
e is an internal reference to setTimeout,
ledger is the array that will be populated*/
var ledger = ledger || [],
probObjCount = 0;
f = f || false,
e && e.value ? clearTimeout(e.value) : void(0);
e = e || {};
e.value = setTimeout(function(){f && f.call(ledger,ledger);},50);
if(obj instanceof Array){
obj.forEach(function(d,i){
(typeof d === "object") && window.requestAnimationFrame(function(){
inspect(d,f,e,ledger);
})
})
return;
}
for (var i in obj) {
if(!obj.hasOwnProperty(i)){
continue;
}
if(typeof obj[i] === "object") {
++probObjCount;
!function(obj){
window.requestAnimationFrame(function(){
inspect(obj,f,e,ledger);
})
}(obj[i]);
}
}
if(probObjCount >= 2){
ledger.push(obj);
}
}
You provide this function 2 things, an object to inspect and a callback that will be provided with a ledger (an array) that will be the list of objects that fits your criteria:
var x = [{"key1":"value", "key2":"value", "key3":[{"key4":"value", "key5":"value"}, { "key6":"value", "key7":"value"}]}];
inspect(x,function(l){console.log(l)});
//after a few seconds:
//[]
var x = [{"key1":"value", "key2":"value", "key3":[{"key4":"value", "key5":"value"}, { "key6":"value", "key7":"value"}], "key8":[{"key9":"value", "key10":"value"}, { "key11":"value", "key12":"value"}]}]
inspect(x,function(l){console.log(l)});
//[{…}]
Above 2 are your examples, now I will turn your 1st example into an example that would be accepted:
var x = [{"key1":"value", "key2":"value", "key3":[{"key4":["value"], "key5":["value"]}, { "key6":"value", "key7":"value"}]}]
inspect(x,function(l){console.log(l)});
//[{…}] you will get {"key4":["value"], "key5":["value"]}
I'm making a dictionary of words, so there are 1,000,000+ words.
The problem comes when I need to store the word constructor. I know this is a reserved word in javascript, but I need to add it to the dictionary.
var dictionary = {}
console.log(dictionary ['word_1'])
//undefined, this is good
console.log(dictionary ['word_2'])
//undefined, this is good
console.log(dictionary ['constructor'])
//[Function: Object]
// this cause initialization code to break
How can I fix this? I could muck with the it like key=key+"_" but that seems bad. Is there anything else I can do?
Instead of using a JS object, you could use the built-in Map type which uses strings/symbols as keys and does not conflict with any existing properties.
Replace
var dictionary = {} with var dictionary = new Map()
Override the constructor key as undefined
According to the MDN Object.prototype page, the only thing that isn't hidden by the __fieldname__ schema is the "constructor field". Thus, you could just initialize your objects via { 'constructor': undefined }.
However, you would have to make sure that in your for .. in statements would filter out all keys with undefined as their value, as it would pick up constructor as a "valid" key (even though it wouldn't before you specifically set it to undefined). I.E.
for(var key in obj) if(obj[key] !== undefined) { /* do things */ }
Check for types when getting/setting
Otherwise, you could just check the type when you 'fetch' or 'store' it. I.E.
function get(obj, key) {
if(typeof obj[key] !== 'function') // optionally, `&& typeof obj[key] !== 'object')`
return obj[key];
else
return undefined;
}
I think you should store all words and translation of them in an array. When you need to translate a word, you can use find method of Array.
For example:
var dict = [
{ word: "abc", translated: "xyz" },
...
];
Then:
var searching_word = "abc";
var translation = dict.find(function (item) {
return item.word == searching_word;
});
console.log(translation.translated);
// --> xyz
To achieve expected result , use below option of using index to get value of any key value
var dictionary = {};
var dictionary1 = {
constructor: "test"
};
//simple function to get key value using index
function getVal(obj, val) {
var keys = Object.keys(obj);
var index = keys.indexOf(val);//get index of key, in our case -contructor
return obj[keys[index]]; // return value using indec of that key
}
console.log(getVal(dictionary, "constructor"));//undefined as expected
console.log(getVal(dictionary1, "constructor"));//test
console.log(dictionary["word_1"]);
//undefined, this is good
console.log(dictionary["word_2"]);
//undefined, this is good
codepen - https://codepen.io/nagasai/pen/LOEGxM
For testing , I gave one object with key-constructor and other object without constructor.
Basically I am getting the index of key first and getting value using index
I was just going through the source code of jquery and came across the following function:
function isArraylike( obj ) {
// Support: iOS 8.2 (not reproducible in simulator)
// `in` check used to prevent JIT error (gh-2145)
// hasOwn isn't used here due to false negatives
// regarding Nodelist length in IE
var length = "length" in obj && obj.length,
type = jQuery.type( obj );
if ( type === "function" || jQuery.isWindow( obj ) ) {
return false;
}
if ( obj.nodeType === 1 && length ) {
return true;
}
return type === "array" || length === 0 ||
typeof length === "number" && length > 0 && ( length - 1 ) in obj;
}
if you go line by line , you will see that jquerys internal methods get called such as $.type and $.isWindow, now the part I don't understand is right at the end, its the following piece of code:
( length - 1 ) in obj;
I see the && used in the end, my only question is when does this check return true and when does it return false? Its really hard to tell, is that check only for when obj is an object or also when obj is an array?
What is that check really really checking for?
I created the following piece of code, to make some sense of that line:
arr = ['hello' , 'world'];
check = (arr - 1) in arr;
console.log(check);
Well I get a false in the console, I have no idea what scenario would return a true . Can somebody shed some light on this please?
This is very simple but has nothing to do with jquery. That's just javascript.
It checks if a key is part of an object or array.
var a=["a","b","c","d"]
has at least four keys 0,1,2 and 3 so you can positively test all those keys, i.e. in the console
0 in a
true
1 in a
true
2 in a
true
3 in a
true
4 in a
false
So (length-1) in obj checks if the last element of the array or object is defined or not.
That is done, as you could define a as
var a={ "0":"a", "3":"d", "length":4 }
which would be a sparse array.
Your test code would be
arr = ['hello' , 'world'];
check = (arr.length - 1) in arr;
console.log(check);
I am curious about an improved way to dynamically delete properties from a javascript object based on wildcards. Firstly, suppose I have the following object:
object =
{
checkbox_description_1 : 'Chatoyant',
checkbox_description_2 : 'Desultory',
random_property : 'Firefly is a great program',
checkbox_mood_1 : 'Efflorescent',
checkbox_description_3 : 'Ephemeral'
}
Task
Now, the end result is to have removed all properties under the guise of
'checkbox_description' and leave the rest of the object intact as shown:
object =
{
random_property : 'Firefly is a great program',
checkbox_mood_1 : 'Efflorescent',
}
My solution
At present my solution involves jquery and the following code:
var strKeyToDelete = 'checkbox_description'
/* Start looping through the object */
$.each(object, function(strKey, strValue) {
/* Check if the key starts with the wildcard key to delete */
if(this.match("^"+strKey) == strKeyToDelete) {
/* Kill... */
delete object[strKey];
};
});
Issue
Something about this seems very inelegant to me and if the object were to be of reasonable size very process intensive. Is there a better way of performing this operation?
This is the bare minimum required:
function deleteFromObject(keyPart, obj){
for (var k in obj){ // Loop through the object
if(~k.indexOf(keyPart)){ // If the current key contains the string we're looking for
delete obj[k]; // Delete obj[key];
}
}
}
var myObject = {
checkbox_description_1 : 'Chatoyant',
checkbox_description_2 : 'Desultory',
random_property : 'Firefly is a great program',
checkbox_mood_1 : 'Efflorescent',
checkbox_description_3 : 'Ephemeral'
};
deleteFromObject('checkbox_description', myObject);
console.log(myObject);
// myObject is now: {random_property: "Firefly is a great program", checkbox_mood_1: "Efflorescent"};
So that's pretty close to the jQuery function you have.
(Though a little faster, considering it doesn't use jQuery, and indexOf instead of match)
So, what's with the ~ before indexOf?
indexOf returns a integer value: -1 if the string is not found, and a index, starting from 0, if it is found. (So always a positive integer if found)
~ is a bitwise NOT, that inverts this output. As it happens to be, the inverted output of indexOf is just what we need to indicate "found" or "not found".
~-1 becomes 0, a false-ish value.
~x, where x is 0 or postitive, becomes -(x+1), a true-ish value.
This way, ~string.indexOf('needle') acts like string.contains('needle'), a function that we don't have in JavaScript.
Additionally, you could add a double boolean not (!!) in front of the ~, to convert the true-ish or false-ish output to a real true / false, but that's not necessary in JavaScript.
Functionally, ~string.indexOf('needle') and !!~string.indexOf('needle') are equal.
In case you specifically need the key to begin with the needle, replace the:
~k.indexOf(keyPart)
With:
k.indexOf(keyPart) === 0
var myObject = {
checkbox_description_1 : 'Chatoyant',
checkbox_description_2 : 'Desultory',
random_property : 'Firefly is a great program',
checkbox_mood_1 : 'Efflorescent',
checkbox_description_3 : 'Ephemeral'
};
const removeProperty = dyProps => ({ [dyProps]: _, ...rest }) => rest;
const remove_random_property = removeProperty('random_property');
console.log(remove_random_property(myObject));
You can use How to check if a string "StartsWith" another string?:
function deleteFromObject(keyToDelete, obj) {
var l = keyToDelete.length;
for (var key in obj)
if (key.substr(0, l) == keyToDelete) // key begins with the keyToDelete
delete obj[key];
}
If you're looking for a solution that doesn't mutate the original object, you can try something like this
const omit = (source = {}, omitKeys = []) => (
Object.keys(source).reduce((output, key) => (
omitKeys.includes(key) ? output : {...output, [key]: source[key]}
), {})
)
Tests
const original = {a:1, b:2, c:3, d:4, e:5}
console.log('original: ', JSON.stringify(original));
const modified = omit(original, ['b', 'd'])
console.log('modified: ', JSON.stringify(modified));
console.log('original: ', JSON.stringify(original));
// Will log:
// original: {"a":1,"b":2,"c":3,"d":4,"e":5}
// modified: {"a":1,"c":3,"e":5}
// original: {"a":1,"b":2,"c":3,"d":4,"e":5}
This will create a new object and spread all properties of the source object into it, except those included in the exception list (omitKeys).
The code is very simple and I would be expecting true however it returns false
var markets = ["AB", "CD"];
console.log("AB" in markets);
I think you're meaning if (markets.indexOf('AB') !== -1). in essentially checks if the test is a property of the object, not if an element is contained within the array.
For more information, look at Array.indexOf vs. the in operator.
Because in looks up property names, not values. Your property names are the array indices.
From MDN's page on the in operator:
The in operator returns true if the specified property is in the specified object.
prop A string or numeric expression representing a property name or array index
Note a property name or array index. The in operator does not search for property values, but for property names. In this case, the property names are 0 and 1, so 0 in markets will return true.
You should use indexOf, in browsers that support it, and shim it in those that don't.
Because in is meant for objects, not arrays. If you want to reliably do this you have to search through each element in the array:
for( var i=0, l=markets.length; i<l; i++ ){
if( markets[i] === 'AB' ){
// do something
}
}
The following will work, which is what you're thinking:
var markets = { AB: 1, CD: 1 };
console.log( "AB" in markets );
In only works when you are using an object, not an array. So this will work:
var markets = {
AB: 'AB',
CD: 'CD'
};
'AB' in markets; // true
As said in won't help you in this case.
I guess you'll have to write a searching function.
Here's one:
function inArray(ArrObj, Search){
var exists = false;
for (var i = 0; i < ArrObj.length; i++){
if (ArrObj[i] == Search){
return true;
var exists = true;
break;
}
else if ((i == (ArrObj.length - 1)) && (!exists)){
if (ArrObj[i] != Search){
return false;
}
}
}
}
I think you want something like this, console.log(markets[0]);