Why is this array undefined when I call .length? - javascript

I am working on an exercise from the book Eloquent JavaScript (see 'a list' at bottom of linked page). Basically, I want to make an object that takes an array and turns it into a list with the following structure:
var list = {
value: 1,
rest: {
value: 2,
rest: {
value: 3,
rest: null
}
}
};
When I run the code below in the console, I get TypeError: Cannot read property 'length' of undefined (line 3 in function arrayToList). Why is the array on which I am calling .length not defined?
function arrayToList(array){
var list = {};
if(!array.length){
list.value = null;
}
else{
list.value = array.shift();
list.rest = arrayToList();
}
return list;
}
console.log(arrayToList([10, 20]));
Note: Stack Overflow question List data structures in JavaScript is a very similar post on the same problem. I'm not looking for a solution here so much as an explanation as to what is going haywire in the recursive call.

list.rest = arrayToList();
Since you don't pass any parameter to arrayToList, array will have the default value undefined. That is why it is failing with
TypeError: Cannot read property 'length' of undefined
So, during the recursive call, you are supposed to pass an array to arrayToList, again.
function arrayToList(array) {
var list = {};
if (!array.length) {
list.value = null;
} else {
list.value = array.shift();
list.rest = arrayToList(array); // Pass the `array` after shifting
}
return list;
}
console.log(arrayToList([10, 20]));
# { value: 10, rest: { value: 20, rest: { value: null } } }

On this line:
list.rest = arrayToList();
… you recursively call the function but you don't specify an argument, so array becomes undefined.
It looks like you should just pass array again (which you have modified).
list.rest = arrayToList(array);
live demo

In the recursive arrayToList call, you're not passing anything as a parameter (!).

function arrayToList(array) {
var list = {};
if (!(array instanceof Array) || array.length == 0) {
list.value = null;
} else {
list.value = array.shift();
list.rest = arrayToList();
}
return list;
}

Related

why is iterator objects variable undefined?

Learning to make objects iterable in Javascript.
Object is:
var arrayLikeObject = {
0: "hello",
1: "there",
2: "crappy coder",
length: 3,
}
then I do this to make it iterable:
arrayLikeObject[Symbol.iterator] = function(){
return {
current: 0, // <---- but... it IS defined.
next() {
// let current = 0; // putting it here makes it work
if(current < this.length) {
let a = current;
current++;
return {done: false, value: this[a]};
}
else {
return {done: true};
}
}
};
};
then when I run it with:
console.log("after making it iterable: ==============");
for(let str of arrayLikeObject) {
console.log(str);
}
I get "current is not defined" But as far as I can see, it is. I just can not understand. I thought functions could see variables outside their scope, but not the other way around, unless they get "overshadowed" if that's the correct terminology. I forgot.
current is not a variable, it is a property, so you would need to reference it as this.current.
However, you have another issue with this:
In this.length and this[a], the this object is not arrayLikeObject, but the object that has the next() method.
You can also fix this, but I think it is simpler to go the other way, and make next an arrow function. That way this.length and this[a] will work as intended. Make current a normal variable within the closure:
var arrayLikeObject = {
0: "hello",
1: "there",
2: "crappy coder",
length: 3,
}
arrayLikeObject[Symbol.iterator] = function(){
let current = 0;
return {
next: () => {
if(current < this.length) {
return {done: false, value: this[current++]};
}
else {
return {done: true};
}
}
};
};
console.log("after making it iterable: ==============");
for(let str of arrayLikeObject) {
console.log(str);
}

Compare value within nested object

So I've been trying to find a solution to this for a little while with no luck.
const nameTest = 'testName';
const test = {
RANDOM_ONE: {
NAME: 'testName',
SOMETHING: {...}
},
RANDOM_TWO: {
NAME: 'Name',
SOMETHING: {...}
}
}
Is there any simple, easy way where I can compare the nameTest and the NAME key without knowing what the RANDOM_X is in order to access NAME?
You can use Object.keys() to get the array of all the keys. Then loop through the array to check the property:
const nameTest = 'testName';
const test = {
RANDOM_ONE: {
NAME: 'testName',
SOMETHING: {}
},
RANDOM_TWO: {
NAME: 'Name',
SOMETHING: {}
}
}
let testKeys = Object.keys(test);
testKeys.forEach(function(k){
console.log(test[k].NAME == nameTest);
});
You can use a for ... in loop:
for (let key in test) {
if (test[key].NAME === nameTest) {
// do something
}
}
I hope we know that 2 levels down into test is your object. You could write a function, to compare the name key.
function compare(obj, text){
for(let x in obj){
if(obj.x.name == text) return true;
else ;
}
}
Then call the function with your object and the string.
let a = compare(test, nameTest);
Note: this would compare the object to only ascertain if it contains the nameTest string.
var obj= test.filter(el){
if(el.NAME==nameTest)
{
return el;
}
}
var x= obj!=null?true:false;
You could use find.
The find method executes the callback function once for each index of
the array until it finds one where callback returns a true value. If
such an element is found, find immediately returns the value of that
element. Otherwise, find returns undefined.
So it is more memory efficient, than looping over the whole object with forEach, because find returns immediately if the callback function finds the value. Breaking the loop of forEach is impossible. In the documentation:
There is no way to stop or break a forEach() loop other than by
throwing an exception. If you need such behavior, the forEach() method
is the wrong tool.
1. If you want to get the whole object
var nameTest = 'testName';
var test = {
RANDOM_ONE: {
NAME: 'testName',
SOMETHING: {}
},
RANDOM_TWO: {
NAME: 'Name',
SOMETHING: {}
}
};
function getObjectByNameProperty(object, property) {
var objectKey = Object.keys(object)
.find(key => object[key].NAME === property);
return object[objectKey];
}
var object = getObjectByNameProperty(test, nameTest);
console.log(object);
2. If you just want to test if the object has the given name value
var nameTest = 'testName';
var test = {
RANDOM_ONE: {
NAME: 'testName',
SOMETHING: {}
},
RANDOM_TWO: {
NAME: 'Name',
SOMETHING: {}
}
};
function doesObjectHaveGivenName(object, nameValue) {
var objectKey = Object.keys(object)
.find(key => object[key].NAME === nameValue);
return objectKey ? true : false;
}
console.log( doesObjectHaveGivenName(test, nameTest) );

JavaScript: Creating a function to reject array elements found to be truthy, trouble with values in objects in the array

I'm working on a code challenge assignment. Create a function, reject, that takes an array and a callback function, and removes from the array any items that are found truthy when the callback function is run against them. I've written the following:
function reject(collection, callback) {
for (var i = 0; i < collection.length; i++) {
if(callback(collection[i]) === true){
collection.splice(i, 1);
}
}
return collection;
}
and where I'm hitting a wall is a test with an array of key-value pairs. The failing test:
var obj = {a:1, b:2, c:3, d:4};
var isOdd = function(value, key, collection) { return value % 2 !== 0; };
var evens = reject(obj, isOdd);
expect(evens).to.eql({b:2, d:4});
Lack of experience has exhausted my ability to search for answers effectively, so here we are. Any help or guidance is appreciated.
Edited to add:
Misread the tests in the original instructions (then failed to catch it when copy/pasting the test). I definitely know the difference between an object and an array, just thought I saw [{a:1, b:2, c:3, d:4}] in the document but it was actually ({a:1, b:2, c:3, d:4}) for whatever reason. Sorry.
I think this is what you're trying to do?
function reject(collection, callback) {
Object.keys(collection).forEach(function(key){
if(callback(collection[key], key, collection)){
delete collection[key];
}
});
return collection;
}
var obj = {a:1, b:2, c:3, d:4};
var isOdd = function(value, key, collection) { return value % 2 !== 0; };
var evens = reject(obj, isOdd);
console.log(evens); //prints { b: 2, d: 4 }
You have the right idea, however you need to look at the difference between a JavaScript object and array. Read this and learn the difference.
A JavaScript object does not have the property of .length to return the size of the collection. Use the following loop instead:
for (var key in collection)
A collection does not have the property .splice, that is for arrays. Instead of using .splice to remove the item use
delete collection[key]
Finally, pass the item in the collection to the callback
callback(collection[key])
Updated Answer:
function reject(collection, callback) {
for (var key in collection) {
if(callback(collection[key]) === true) {
delete collection[key];
}
}
return collection;
}
var obj = {a:1, b:2, c:3, d:4}; // Use for an object passed
var obj2 = [1, 2, 3, 4]; // Use as an array passed
var isOdd = function(value) { return value % 2 !== 0; };
var evens = reject(obj, isOdd);
console.log(evens);
// expect(evens).to.eql({b:2, d:4});
Here's the solution I ended up with. To clarify, the tests were passing in arrays and objects, so that's why I first had trouble (with the objects) and then there was some confusion in in the answers. I wrote:
function reject(collection, callback) {
if(Array.isArray(collection)){
for (var i = 0; i < collection.length; i++) {
if(callback(collection[i]) === true){
collection.splice(i, 1);
}
}
} else {
for (number in collection){
if(callback(collection[number]) === true){
delete collection[number];
}
}
};
return collection;
}
I know it could likely be much cleaner, but just for the sake of clarity, I wanted to show a solution that works.
I have created a not() function, which accepts a function and can be passed to a filter function:
// wraps passed function, so that when called,
// the return value will be negated
function not(a){
return function(){
return !a.apply(a, arguments);
}
}
Usage:
// some sample data
var animals = [
{name: 'Pete', type: 'fish'},
{name: 'Jim', type: 'fish'},
{name: 'Tom', type: 'cat'}
];
// a simple filter callback
var isCat = function(animal){
return animal.type === 'cat';
};
// gather all animals, which are not cats
var fishes = animals.filter(not(isCat));
I know it could likely be much cleaner
Just for the fun, here is how I alter the answer step by step to make it a bit cleaner.
Making it immutable
Changing the collection will change the original object, which is passed to the function,
since arrays and objects are reference types. To solve this, you can clone the collection and work
on that, or you can copy the elements, which are not rejected by the callback. I'm doing the latter.
function reject(collection, callback) {
var ret;
if(Array.isArray(collection)){
ret = [];
for (var i = 0; i < collection.length; i++) {
if(!callback(collection[i])){
ret.push(collection[i]);
}
}
} else {
ret = {};
for (number in collection){
if(!callback(collection[number])){
ret[number] = collection[number];
}
}
}
return ret;
}
Shortening with ES5
The loops' mechanics and the actual code done by the loop is entangled, we can have a much cleaner
code, if we stop concentrating on how to write a loop and let JS do it. For example: notice how
I refer to the individual elements of the array collection as value, instead of collection[i].
function reject(collection, callback) {
var ret;
if(Array.isArray(collection)){
ret = [];
collection.forEach(function(value){
if(!callback(value)){
ret.push(value);
}
});
} else {
ret = {};
Object.keys(collection).forEach(function(key){
var value = collection[key];
if(!callback(value)){
ret[key] = value;
}
});
}
return ret;
}
Changing if to filter()
Array.prototype.filter() is a bit more useful for us, than forEach, since in the core of the loop you can
simply return a truthy or falsy value and filter will handle collecting the data to a new array based on
that automatically for you.
function reject(collection, callback) {
var ret;
if(Array.isArray(collection)){
ret = collection.filter(function(value){
return !callback(value);
});
} else {
ret = {};
Object.keys(collection).filter(function(key){
return !callback(collection[key]);
}).forEach(function(key){
ret[key] = collection[key];
});
}
return ret;
}
Using reduce for objects
The goal would be to minimize functions, which go outside from their scope in order to work correctly.
In the objects part we can use Array.prototype.reduce() instead of forEach() and simply return it's
output directly to the ret value, when we are done, just as we did in the Array part with filter().
function reject(collection, callback) {
var ret;
if(Array.isArray(collection)){
ret = collection.filter(function(value){
return !callback(value);
});
} else {
ret = Object.keys(collection).filter(function(key){
return !callback(collection[key]);
}).reduce(function(obj, key){
obj[key] = collection[key];
return obj;
}, {});
}
return ret;
}
Shortening functions with ES6
Since we are already using Array.isArray(), which is an ES6 method, we can try using arrow functions
to compress anonymus functions.
function reject(collection, callback) {
var ret;
if(Array.isArray(collection)){
ret = collection.filter(value => !callback(value));
} else {
ret = Object.keys(collection)
.filter(key => !callback(collection[key]))
.reduce((obj, key) => {
obj[key] = collection[key];
return obj;
}, {})
;
}
return ret;
}
We don't need the ret variable
We previously removed the need to access the ret value in our logics, we can use a ternary operator
to directly return the value generated by the expressions.
function reject(collection, callback) {
return (
Array.isArray(collection)
? collection
.filter(value => !callback(value))
: Object.keys(collection)
.filter(key => !callback(collection[key]))
.reduce((obj, key) => {
obj[key] = collection[key];
return obj;
}, {})
)
}

Object has-property-deep check in JavaScript

Let's say we have this JavaScript object:
var object = {
innerObject:{
deepObject:{
value:'Here am I'
}
}
};
How can we check if value property exists?
I can see only two ways:
First one:
if(object && object.innerObject && object.innerObject.deepObject && object.innerObject.deepObject.value) {
console.log('We found it!');
}
Second one:
if(object.hasOwnProperty('innerObject') && object.innerObject.hasOwnProperty('deepObject') && object.innerObject.deepObject.hasOwnProperty('value')) {
console.log('We found it too!');
}
But is there a way to do a deep check? Let's say, something like:
object['innerObject.deepObject.value']
or
object.hasOwnProperty('innerObject.deepObject.value')
There isn't a built-in way for this kind of check, but you can implement it easily. Create a function, pass a string representing the property path, split the path by ., and iterate over this path:
Object.prototype.hasOwnNestedProperty = function(propertyPath) {
if (!propertyPath)
return false;
var properties = propertyPath.split('.');
var obj = this;
for (var i = 0; i < properties.length; i++) {
var prop = properties[i];
if (!obj || !obj.hasOwnProperty(prop)) {
return false;
} else {
obj = obj[prop];
}
}
return true;
};
// Usage:
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
}
console.log(obj.hasOwnNestedProperty('innerObject.deepObject.value'));
You could make a recursive method to do this.
The method would iterate (recursively) on all 'object' properties of the object you pass in and return true as soon as it finds one that contains the property you pass in. If no object contains such property, it returns false.
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
};
function hasOwnDeepProperty(obj, prop) {
if (typeof obj === 'object' && obj !== null) { // only performs property checks on objects (taking care of the corner case for null as well)
if (obj.hasOwnProperty(prop)) { // if this object already contains the property, we are done
return true;
}
for (var p in obj) { // otherwise iterate on all the properties of this object.
if (obj.hasOwnProperty(p) && // and as soon as you find the property you are looking for, return true
hasOwnDeepProperty(obj[p], prop)) {
return true;
}
}
}
return false;
}
console.log(hasOwnDeepProperty(obj, 'value')); // true
console.log(hasOwnDeepProperty(obj, 'another')); // false
Alternative recursive function:
Loops over all object keys. For any key it checks if it is an object, and if so, calls itself recursively.
Otherwise, it returns an array with true, false, false for any key with the name propName.
The .reduce then rolls up the array through an or statement.
function deepCheck(obj,propName) {
if obj.hasOwnProperty(propName) { // Performance improvement (thanks to #nem's solution)
return true;
}
return Object.keys(obj) // Turns keys of object into array of strings
.map(prop => { // Loop over the array
if (typeof obj[prop] == 'object') { // If property is object,
return deepCheck(obj[prop],propName); // call recursively
} else {
return (prop == propName); // Return true or false
}
}) // The result is an array like [false, false, true, false]
.reduce(function(previousValue, currentValue, index, array) {
return previousValue || currentValue;
} // Do an 'or', or comparison of everything in the array.
// It returns true if at least one value is true.
)
}
deepCheck(object,'value'); // === true
PS: nem035's answer showed how it could be more performant: his solution breaks off at the first found 'value.'
My approach would be using try/catch blocks. Because I don't like to pass deep property paths in strings. I'm a lazy guy who likes autocompletion :)
JavaScript objects are evaluated on runtime. So if you return your object statement in a callback function, that statement is not going to be evaluated until callback function is invoked.
So this function just wraps the callback function inside a try catch statement. If it catches the exception returns false.
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
};
const validate = (cb) => {
try {
return cb();
} catch (e) {
return false;
}
}
if (validate(() => obj.innerObject.deepObject.value)) {
// Is going to work
}
if (validate(() => obj.x.y.z)) {
// Is not going to work
}
When it comes to performance, it's hard to say which approach is better.
On my tests if the object properties exist and the statement is successful I noticed using try/catch can be 2x 3x times faster than splitting string to keys and checking if keys exist in the object.
But if the property doesn't exist at some point, prototype approach returns the result almost 7x times faster.
See the test yourself: https://jsfiddle.net/yatki/382qoy13/2/
You can also check the library I wrote here: https://github.com/yatki/try-to-validate
I use try-catch:
var object = {
innerObject:{
deepObject:{
value:'Here am I'
}
}
};
var object2 = {
a: 10
}
let exist = false, exist2 = false;
try {
exist = !!object.innerObject.deepObject.value
exist2 = !!object2.innerObject.deepObject.value
}
catch(e) {
}
console.log(exist);
console.log(exist2);
Try this nice and easy solution:
public hasOwnDeepProperty(obj, path)
{
for (var i = 0, path = path.split('.'), len = path.length; i < len; i++)
{
obj = obj[path[i]];
if (!obj) return false;
};
return true;
}
In case you are writing JavaScript for Node.js, then there is an assert module with a 'deepEqual' method:
const assert = require('assert');
assert.deepEqual(testedObject, {
innerObject:{
deepObject:{
value:'Here am I'
}
}
});
I have created a very simple function for this using the recursive and happy flow coding strategy. It is also nice to add it to the Object.prototype (with enumerate:false!!) in order to have it available for all objects.
function objectHasOwnNestedProperty(obj, keys)
{
if (!obj || typeof obj !== 'object')
{
return false;
}
if(typeof keys === 'string')
{
keys = keys.split('.');
}
if(!Array.isArray(keys))
{
return false;
}
if(keys.length == 0)
{
return Object.keys(obj).length > 0;
}
var first_key = keys.shift();
if(!obj.hasOwnProperty(first_key))
{
return false;
}
if(keys.length == 0)
{
return true;
}
return objectHasOwnNestedProperty(obj[first_key],keys);
}
Object.defineProperty(Object.prototype, 'hasOwnNestedProperty',
{
value: function () { return objectHasOwnNestedProperty(this, ...arguments); },
enumerable: false
});

List data structures in JavaScript

In an exercise in the book Eloquent JavaScript I need to create a list data structure (as below) based on the array [1, 2, 3].
The tutorial JavaScript Data Structures - The Linked List shows how to do this, but I don't really understand the intention to create this.start and this.end variables inside the tutorial.
var list = {
value: 1,
rest: {
value: 2,
rest: {
value: 3,
rest: null
}
}
};
I tried to solve this via the code below.
function arrayToList(array){
var list = { value:null, rest:null};
for(i=0; i<array.length-1; i++)
list.value = array[i];
list.rest = list;
return list;
}
This code gives me an infinite loop of array[0]. What's wrong with my code?
This tutorial shows how to do this but I don't really understand the intention to create this.start and this.end variables inside the tutorial.
The tutorial uses a List wrapper around that recursive structure with some helper methods. It says: "It is possible to avoid having to record the end of the list by performing a traverse of the entire list each time you need to access the end - but in most cases storing a reference to the end of the list is more economical."
This code gives me an infinite loop of array[0].
Not really, but it creates a circular reference with the line list.rest = list;. Probably the code that is outputting your list chokes on that.
What's wrong is with my code?
You need to create multiple objects, define the object literal inside the loop body instead of assigning to the very same object over and over! Also, you should access array[i] inside the loop instead of array[0] only:
function arrayToList(array){
var list = null;
for (var i=array.length-1; i>=0; i--)
list = {value: array[i], rest:list};
return list;
}
This particular data structure is more commonly called cons. Recursion is the most natural (not necessarily the most efficient) way to work with conses. First, let's define some helper functions (using LISP notation rather than "value/rest"):
function cons(car, cdr) { return [car, cdr] }
function car(a) { return a[0] }
function cdr(a) { return a[1] }
Now, to build a cons from an array, use the following recursive statement:
cons-from-array = cons [ first element, cons-from-array [ the rest ] ]
In Javascript:
function arrayToList(array) {
if(!array.length)
return null;
return cons(array[0], arrayToList(array.slice(1)));
}
And the reverse function is similarly trivial:
function listToArray(list) {
if(!list)
return [];
return [car(list)].concat(listToArray(cdr(list)));
}
function arrayToList (arr) {
var list = null;
for (var i = arr.length - 1; i >= 0; i--) {
list = {
value: arr[i],
rest: list
};
}
return list;
}
function prepend (elem, list) {
return {
value: elem,
rest: list
};
}
function listToArray (list) {
var arr = [];
for (var node = list; node; node = node.rest) {
arr.push(node.value);
}
return arr;
}
function nth(list, num) {
if (!list) {
return undefined;
} else if (num === 0) {
return list.value;
} else {
return nth(list.rest, num - 1);
}
}

Categories

Resources