I have a for/in loop that I would like to know if I'm at the end of. I'm not entirely sure how this would work?
My code is:
for (const key in posts.val()) {
if(posts.val()[key][postId] != undefined) {
found = true;
//do something
} else {
// if at end && found !== true
}
}
Any idea how I would know if I'm at the end of the for/in loop?
Thank you!
There's no built-in functionality for something like that. Assuming the properties don't get mutated when .val() is called, you could call .val() initially to get the object, count up the number of keys (including inherited keys) with for..in, and then use for..in again to iterate and be able to test how close you are to the end of the loop:
const val = posts.val();
let totalKeyCount = 0;
for (const _ in posts.val()) {
totalKeyCount++;
}
let keyIndex = 0;
for (const key in posts.val()) {
keyIndex++;
if(val[key][postId] != undefined) {
found = true;
//do something
} else {
if (keyIndex === totalKeyCount) {
console.log('last iteration');
}
}
}
But, if you don't depend on inherited keys, it would be better to use Object.keys, which returns an array, and upon which array methods can be used.
Assuming you're trying to find a particular value in the object (and on the object itself), you can use .find on Object.values instead:
const foundPost = Object.values(posts.val()).find(item => item[postId] !== undefined);
// if an item[postId], if it exists, will be truthy, simplify to `item => item[postId]`
if (foundPost) {
// do something with the found post
} else {
// all iterations have been completed, and nothing was found
}
Also note that the order of properties iterated on in a for..in loop is not entirely reliable. As MDN says:
A for...in loop iterates over the properties of an object in an arbitrary order (see the delete operator for more on why one cannot depend on the seeming orderliness of iteration, at least in a cross-browser setting).
There's no real notion of end of an object unless you mean the last key to iterate. You can get the object's keys, iterate, and check the index against keys.length-1:
let obj = posts.val();
Object.keys(obj).forEach((k, i, keys) => {
if(obj[k][postId] !== undefined) {
let found = true;
//do something
} else {
let atEnd = (i === keys.length-1);
// if at end && found !== true
}
});
Typically, finding something looks like this:
function isFound(obj, cmpFn) {
let found = false;
for (let k in obj) {
if (cmpFn(obj[k]) === true) {
found = true;
break;
}
}
return found;
}
Or:
function isFound(obj, cmpFn) {
return Object.keys(obj)
.some(k => cmpFn(obj[k]));
}
You should have both keys somewhere, so you can avoid iteration entirely:
let isFound = (id_b in obj[id_a]);
The above works if you don't store empty entries in the table, which is usually desirable.
I'd also recommend using !== over != to avoid coercion and ambiguity.
Related
I am basically trying to get this problem to work and have isolated the issue to line 21. I think the way I'm trying to access the object key is wrong. I am simply trying to say: if the object key in the new object exists in the original array, push the new value from the new object into the new array.
Edit to add code block
function valueReplace(array, obj) {
var replaced = [];
for (var i = 0; i < array.length; i += 1) {
var value = obj[array[i]];
if (array.indexOf(obj.i) !== -1) {
replaced.push(value);
} else {
replaced.push(array[i]);
}
}
return replaced;
}
You have a mixed up error report, but at the actual code, you try to access the object with the property i, obj.i, which not exists. Read more about property accessor.
For getting the wanted result, you might use the in operator for checking if a property in an object exists.
if (array[i] in obj) {
replaced.push(obj[array[i]]);
} else {
replaced.push(array[i]);
}
It looks like one of the issues you are having is trying to access a dynamic property with dot notation, which JS generally doesn't like. I reversed your logical if because IMO it makes more sense to see if the object has a property than get a property and then get the index of the array, but you could reverse it back to using index of by array.indexOf(obj[i]) !== -1
function valueReplace(array, obj) {
let replaced = [];
for (let i = 0, len = array.length; i < len; i++) {
if (obj.hasOwnProperty(array[i])) {
replaced.push(obj[array[i]]);
} else {
replaced.push(array[i]);
}
}
return replaced;
}
Because I generally like simplifying things here is this functionality rewritten in ES6 compatible code, using array.prototype.map. Don't use it for your homework, but if you want you can work it backwards into a standard function ;).
const valueReplace = (array, obj) => array.map(val => (obj.hasOwnProperty(val)) ? obj[val] : val);
I need to loop through an objects keys and if the property is false then delete the keys property. This is what I have so far but I can't figure out what I am doing wrong.
function onlyTheTruthy() {
onlyTheTruthy.key;
var prop;
for (key in onlyTheTruthy){
if (key != true) {
delete onlyTheTruthy.key.prop
}
else {
}
}
return onlyTheTruthy;
};
In a for...in loop, use the key to access the value on the object. This is done with brackets like obj[key].
function onlyTheTruthy(obj) {
for (var key in obj) {
if (!obj[key]) {
delete obj[key];
}
}
return obj;
}
function onlyTheTruthy() {
onlyTheTruthy.key;
var prop;
for (key in onlyTheTruthy) {
if (key != true) {
delete onlyTheTruthy.key.prop
} else {}
}
return onlyTheTruthy;
};
This is your code.
Line:
You declare a function named onlyTheTruthy, taking no arguments. However, I think it ought to take one: o.
This line does nothing. It should be removed.
You set the scope of the variable prop, but give it no value. Fine.
A for...in loop. You may want to research exactly what that does. The key variable hasn't been seen before, other than on line 2, but that is a different variable: a property of onlyTheTruthy. Also, you're looping through the keys of the function you're inside of: onlyTheTruthy. I'm confident this isn't what you want. Try:
for (prop in o) {
Checking if key is not equal to true. Not too often are boolean values used as object keys. Maybe:
if (!o[prop]) { // Or, more verbosely:
if (o[prop] === false) {
Look up square brackets ([]).
Empty else. How about no else? It's not required, you know.
} Closing the for...in loop.
Returning the function itself. Nope. How about o? Or, if you end up modifying the object itself, there's no need for a return at all.
No semicolon necessary at the end of a function declaration.
Happy Coding!
Fixed version:
function onlyTheTruthy (o) {
for (var prop in o) {
if (!o[prop])
delete o[prop];
}
}
I have some simple objects in an Array. I want to add new objects to the Array only if they are not already in it, based on an object property.
var o = {text: 'foo'}
var a = [o]
var checkExisting = function (list, obj) {
list.forEach(function(elem) {
if (elem.text === obj) {
return true
}
}
}
checkExisting(a, 'foo')
This doesn't work. I can't figure out why. Any help or alternatives would be great.
Because you can't return value from callback in forEach, you can use for, like this
var checkExisting = function (list, obj) {
for (var i = 0, len = list.length; i < len; i++) {
if (list[i].text === obj) {
return true;
}
}
}
This can be done very similar to how you are but with .every() because .forEach() always returns undefined. So you can't chain it either.
.every() runs a method over every element like .forEach() but if it receives a false value it will abort. Finally if every iteration returns true it will return true to the caller otherwise it will return false.
Because we return false to make it abort when a value is found (so it wont keep iterating), we then have to flip the value it returns before we return it from checkExisting.
So using that you could do something like:
var checkExisting = function (list, obj) {
return !list.every(function(elem) {
return elem.text !== obj;
});
}
Obviously you would have to extend that for error handling if the object doesn't have a property text etc.
See fiddle: http://jsfiddle.net/reLsqhkm/
And docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every
I wanted to write one version of a function that iterated over both Array objects and Objects, with minimal duplicate code. Something like:
function (arr_or_obj) {
arr_or_obj.forEach( function(key,value) {
// do stuff with key and value
});
}
This was before I realized that (in Chrome at least), Object.keys returns a list of the keys for an array. So I could use that to treat the Array like an object. Like so:
function (arr_or_obj) {
var keys = Object.keys(arr_or_obj);
keys.forEach( function(key) {
var value = arr_or_obj[key];
// do stuff with key and value
});
}
Problem solved. But this was not before I wrote my own "JavaScript pseudo-Array iterator". The only advantage of this was that instead of getting a list of keys for the Array (which could be very long), we save memory by producing only the return values we need. Like so:
function create_iterator(max) {
var jkeys = {};
jkeys.count_ = 0;
jkeys.length = max;
jkeys.getter_ = function() {
var returnable = jkeys.count_;
jkeys.count_ += 1;
jkeys.__defineGetter__(jkeys.count_,jkeys.getter_);
delete jkeys[jkeys.count_-1];
return returnable;
};
jkeys.__defineGetter__(0, jkeys.getter_);
return jkeys;
}
Which you can then call by going:
var z = create_iterator(100);
z[0];
>> 0
z[0];
>> undefined
z[1];
>> 1;
z[2]
>> 2;
...
This is sort of a question and answer in one, but the obvious question is, is there a better way to do this without using Object.keys?
Object.keys gives you an array of the object's own enumerable properties. That is, properties it has itself, not from its prototype, and that are enumerable (so not including things like length on arrays, which is a non-enumerable property).
You can do the same thing with for-in if you use correct safeguards:
var key;
for (key in arr_or_obj) {
if (arr_or_obj.hasOwnProperty(key)) {
// do something with it
}
}
Now you're doing the same thing you were doing with Object.keys.
But, note that this (and Object.keys) will include properties people put on arrays that aren't array indexes (something which is perfectly valid in JavaScript). E.g.:
var key;
var a = [1, 2, 3];
a.foo = "bar";
for (key in a) {
if (a.hasOwnProperty(key)) {
console.log(key); // Will show "foo" as well as "0", "1", and "2"
}
}
If you want to only process array indexes and not other properties, you'll need to know whether the thing is an array, and then if so use the "is this an index" test:
var key;
var isArray = Array.isArray(arr_or_obj);
// Or (see below):
//var isArray = Object.prototype.toString.call(arr_or_obj) === "[object Array]";
for (key in arr_or_obj) {
if (a.hasOwnProperty(key) &&
(!isArray || isArrayIndex(key))
) {
console.log(key); // Will only show "0", "1", and "2" for `a` above
}
}
...where Array.isArray is a new feature of ES5 which is easily shimmed for older browsers if necessary:
if (!Array.isArray) {
Array.isArray = (function() {
var toString = Object.prototype.toString;
function isArray(arg) {
return toString.call(arg) === "[object Array]";
}
return isArray;
})();
}
...and isArrayIndex is:
function isArrayIndex(key) {
return /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294; // 2^32 - 2
}
See this other answer for details on that (where the magic numbers come from, etc.).
That loop above will loop through an object's own enumerable properties (all of them) if it's not an array, and loop through the indexes (but not other properties) if it's an array.
Taking it a step further, what if you want to include the properties the object gets from its prototype? Object.keys omits those (by design). If you want to include them, just don't use hasOwnProperty in the for-in loop.
You can try something like this...
Live Demo
function forIn(obj) {
var isArray = Array.isArray(obj),
temp = isArray ? obj : Object.keys(obj);
temp.forEach(function (value, index, array) {
console.log(this[isArray ? index : value]);
}, obj);
}
You can write:
for (key in arr_or_obj) {
if (arr_or_obj.hasOwnProperty(key) {
value = arr_or_obj[key];
// do stuff with key and value
}
}
This is so simple I am baffled. I have the following:
var x = 'shrimp';
var stypes = new Array('shrimp', 'crabs', 'oysters', 'fin_fish', 'crawfish', 'alligator');
for (t in stypes) {
if (stypes[t] != x) {
alert(stypes[t]);
}
}
Once the values have iterated it starts returning a dozen functions like
function (iterator, context) {
var index = 0;
iterator = iterator.bind(context);
try {
this._each(function (value) {iterator(value, index++);});
} catch (e) {
if (e != $break) {
throw e;
}
}
return this;
}
What the heck is going on?
Edit: In these scripts I am using http://script.aculo.us/prototype.js and http://script.aculo.us/scriptaculous.js I remember now reading about the way prototype extends arrays and I am betting this is part of it. How do I deal with it?
The for enumeration is going to go over every member of the object you passed it. In this case an array, which happens to have functions as members as well as the elements passed.
You could re-write your for loop to check if typeof stypes[t] == "function" or yada yada. But IMO you are better off just modifying your looping to only elements..
for(var i = 0, t; t = stypes[i]; ++i){
if (t != x) {
alert(t);
}
}
Or
for(var i = 0; i < stypes.length; ++i){
if (stypes[i] != x) {
alert(stypes[i]);
}
}
I wanted to migrate my last comment up to the answer to add the notice of the a caveat for the first type of loop.
from Simon Willison's "A re-introduction to JavaScript"..
for (var i = 0, item; item = a[i]; i++) {
// Do something with item
}
Here we are setting up two variables.
The assignment in the middle part of
the for loop is also tested for
truthfulness - if it succeeds, the
loop continues. Since i is incremented
each time, items from the array will
be assigned to item in sequential
order. The loop stops when a "falsy"
item is found (such as undefined).
Note that this trick should only be
used for arrays which you know do not
contain "falsy" values (arrays of
objects or DOM nodes for example). If
you are iterating over numeric data
that might include a 0 or string data
that might include the empty string
you should use the i, j idiom instead.
you want to do:
for (var i in object) {
if (!object.hasOwnProperty(i))
continue;
... do stuff ...
}
As for..in enumeration iterates over all properties (enumerable or otherwise) that exist on both the object and its prototype chain. The hasOwnProperty check restricts iteration to just those properties on the actual object you want to enumerate.
ES5 makes things a little better for library developers (and help avoid this stuff) but we won't see that ina shipping browser for quite a while :-(
[edit: replacing return with continue. lalalalala ;) ]
Since prototype has extended the array for your convenience you should take advantage of it. Your example could be rewritten as:
var x = 'shrimp';
var stypes = new Array('shrimp', 'crabs', 'oysters', 'fin_fish', 'crawfish', 'alligator');
stypes.without(x).each(alert);
It should be
for (t in stypes) {
if (t != x) {
alert(t);
}
}