I have following code:
var favourites = JSON.parse(localStorage.getItem("favourites"));
Service.all().then(function (multiple) {
var array = [];
for (var i = 0; i < multiple.length; i++) {
for (var j = 0; j < favourites.length; j++) {
if (favourites[j].checked === true) {
if (multiple[i].Name === favourites[j].name) {
Service.getAllBySomething(multiple[i].Id).then(function (resources) {
var arrayOfSomething = [];
for (var k = 0; k < resources.length; k++) {
arrayOfSomething.push(resources[k].ResourceCategoryId);
}
arrayOfSomething = arrayOfSomething .filter(function (elem, pos, arr) {
return arr.indexOf(elem) == pos;
});
multiple[i].existingProperty= arrayOfSomething;
});
array.push(multiple[i]);
}
}
}
}
$scope.vendors = array;
});
My problem is that it says everytime 'Cannot set property existingProperty of undefined'. And I don't know why multiple[i] should be undefined at this line:
multiple[i].existingProperty= arrayOfSomething;
The property exists, I am sure. And it is defined, it is an empty array. And this empty array I want to replace with my made array in the loops.
Where is the fault? How I can fill the existingProperty with my array?
is Service.getAllBySomething asynchronous by any chance? Because in that case, by the time the callback function runs, i (captured in a closure only) has been moved to the end of the array.
Use an additional closure to capture the value of i at the time of dispatching the async call, like this:
Service.getAllBySomething(multiple[i].Id).then(function(i){
return function (resources) {
var arrayOfSomething = [];
for (var k = 0; k < resources.length; k++) {
arrayOfSomething.push(resources[k].ResourceCategoryId);
}
arrayOfSomething = arrayOfSomething .filter(function (elem, pos, arr) {
return arr.indexOf(elem) == pos;
});
multiple[i].existingProperty= arrayOfSomething;
};
}() );
Note the new function receiving i as a param and returning the function you previously used. I'm invoking immediately (an IIFE) to return the function to pass as callback. Inside that function the value of i will be the same as when you dispatch this async call, as it was copied when used as a parameter.
Related
Lets say I have the following functions declared
function curry(fn) {
var args = [];
// push everything but function itself into args
for (var i = 1; i < arguments.length; ++i) {
args.push(arguments[i]);
}
return function() {
var args2 = [];
for (var i = 0; i < arguments.length; i++) {
args2.push(arguments[i]);
}
var argstotal = args.concat(args2);
return fn.apply(argstotal);
};
}
function add(a,b){return a + b};
What im trying to do is obvious, I want to curry the add function which works great itself
var addCurry = curry(add);
addCurry(10, 20, 12314) // should deliver 30, gives NaN
Somehow it returns NaN and I dont know what Im doing wrong... Anybody got an idea?
You have an off-by-one error here: return fn.apply(argstotal)
The first argument to Function.prototype.apply is a scope, the value of this within the invocation, not the first argument. If you were to print out the arguments from within your curried function, you would see:
function curry(fn) {
var args = [];
// push everything but function itself into args
for (var i = 1; i < arguments.length; ++i) {
args.push(arguments[i]);
}
return function() {
var args2 = [];
for (var i = 0; i < arguments.length; i++) {
args2.push(arguments[i]);
}
var argstotal = args.concat(args2);
return fn.apply(argstotal);
};
}
function add(a, b) {
console.log('debug', this, arguments);
return a + b;
}
var cadd = curry(add, 1);
cadd(2);
You can very easily fix this in two ways:
Pass this through to the curried function
Ignore this and do not set it for curried functions
The first option is probably better, as it will surprise developers less. You can implement that using:
function curry(fn) {
var args = [];
// push everything but function itself into args
for (var i = 1; i < arguments.length; ++i) {
args.push(arguments[i]);
}
return function() {
var args2 = [];
for (var i = 0; i < arguments.length; i++) {
args2.push(arguments[i]);
}
var argstotal = args.concat(args2);
return fn.apply(this, argstotal); // <-- only change
};
}
function add(a, b) {
console.log('debug', /* this, */ arguments);
return a + b;
}
var cadd = curry(add, 1);
cadd(2);
The second option can be implemented with fn.apply(null, argstotal).
I have an async function named populateFavoriteItem in for loop :
var result = [];
for (var i = 0; i <array.length ; i++) {
populateFavoriteItem(accountId,array[i],function(doc){
result.push(doc);
//mark 1
})
// if (i == array.length - 1) {
// console.log(result);
// callback(result);
// }
}
//mark 2
console.log(result);
callback(result);
It always run mark 2 first and then run to mark 1. Therefore, I get a null result array. Seems that result is not the populateFavoriteItem callback function.
Im trying to write if() condition in the for loop but get the same consequence, what should I do?
Use Node.js eventEmitter:
var result = [];
var obj;
var j = 0;
var myEventEmitter = new eventEmitter;
myEventEmitter.on('next',addResult);
function addResult(){
result.push(obj)
j++;
if (j == array.length) {
callback(result);
}
}
var populateFav = promiseify(populateFavoriteItem);
for (var i = 0; i <array.length ; i++) {
var ii = i;
populateFavoriteItem(accountId,array[ii],function(doc){
//result.push(doc);
obj = doc;
myEventEmitter.emit("next");
})
}
In the following code there is a console log of obj['mn'] which returns the length of that specific object which is 2. The problem with the code is that it doesn't count the multidimentional array, and only it counts the first array. The result should be 4 because there are 4 'mn' in total. What am I doing wrong?
var arr = [['ab','pq','mn','ab','mn','ab'],'mn','mn'];
var obj = { };
for (var i = 0, j = arr.length; i < j; i++) {
if (obj[arr[i]]) {
obj[arr[i]]++;
}
}
console.log(obj['mn']);
This is what you're looking for:
var arr = [['ab','pq','mn','ab','mn','ab'],'mn','mn'];
var obj = { };
function count(arr, obj) {
for (var i = 0, j = arr.length; i < j; i++) {
if (Array.isArray(arr[i])) {
count(arr[i], obj);
}
else if (typeof obj[arr[i]] !== 'undefined') {
obj[arr[i]]++;
}
else {
obj[arr[i]] = 1;
}
}
return obj;
}
console.log(count(arr, obj));
This is a recursive implementation. When it gets to an array, the recursion get one level deeper.
You are calling obj[['ab','pq','mn','ab','mn','ab']], which is obviously not what you wanted.
You need a depth first search.
If arr[i] is an array, then you need to loop through that array.
I am trying to solve a freeCodeCamp exercise and have gotten stuck. The goal of the exercise is this: Make a function that looks through an array of objects (first argument) and returns an array of all objects that have matching property and value pairs (second argument). Each property and value pair of the source object has to be present in the object from the collection if it is to be included in the returned array.
So what I did, was to make an array of the key pairs of the collection, and another array with the key pairs of the source. The I nested for-loops in order to find matching keys, and if those keys are found, then compare the properties.
But somehow, my code returns no matches.
var collection = [{
first: "Romeo",
last: "Montague"
}, {
first: "Mercutio",
last: null
}, {
first: "Tybalt",
last: "Capulet"
}];
var source = {
last: "Capulet"
};
var collectionKeys = [];
for (var i = 0; i < collection.length; i++) {
collectionKeys.push(Object.keys(collection[i]));
}
var sourceKeys = Object.keys(source);
//for every key pair
for (var t = 0; t < collectionKeys.length; t++) {
//for every key in key pair
for (var x = 0; x < collectionKeys[t].length; x++) {
//for every key in search
for (var y = 0; y < sourceKeys.length; y++) {
//see if a key matches
if (sourceKeys[y] == collectionKeys[t][x]) {
//see if the value matches
if (collection[collectionKeys[t][x]] == source[sourceKeys[y]]) {
console.log(collection[t]);
} else {
console.log("value not found");
}
} else {
console.log("key not found");
}
}
}
}
Can anybody point out what I'm doing wrong?
I've also created a JSfiddle if you want to tinker.
I was also stuck on this for a good hour, when I stumbled upon a couple resources to assist.
I found that rather than the mess of nested for loops, I could use the built in looping methods to greatly simplify my code.
here is where I found my explanation:
https://github.com/Rafase282/My-FreeCodeCamp-Code/wiki/Bonfire-Where-art-thou
function where(collection, source) {
var arr = [];
var keys = Object.keys(source);
// Filter array and remove the ones that do not have the keys from source.
arr = collection.filter(function(obj) {
//Use the Array method every() instead of a for loop to check for every key from source.
return keys.every(function(key) {
// Check if the object has the property and the same value.
return obj.hasOwnProperty(key) && obj[key] === source[key];
});
});
return arr;
}
be more explicit in your declarations - helps to read the code easier:
var sourceKeys = Object.keys(source),
i = 0,
j = 0,
collectionLength = collection.length,
sourceKeysLength = sourceKeys.length;
while (i < collectionLength) {
j = 0;
while (j < sourceKeysLength) {
if (sourceKeys[j] in collection[i] && source[sourceKeys[j]] === collection[i][sourceKeys[j]]) {
console.log('found one!');
}
j++;
}
i++;
}
https://jsfiddle.net/fullcrimp/1cyy8z64/
Some insight here with clear understanding and less loops.
some new javascript function like some, filter, map are really handy to make code tidier as well.
function whatIsInAName(collection, source) {
// What's in a name?
var arr = [];
// Only change code below this line
collection.some(function(obj){
var sk = Object.keys(source); //keys of source object
var sv = Object.values(source); //values of source object
var temp = 0;
for(i=0;i<sk.length;i++){ // run until the number of source properties length is reached.
if(obj.hasOwnProperty(sk[i]) && obj[sk[i]] === sv[i]){ // if it has the same properties and value as parent object from collection
temp++; //temp value is increased to track if it has matched all the properties in an object
}
}
if(sk.length === temp){ //if the number of iteration has matched the temp value
arr.push(obj);
temp = 0; // make temp zero so as to count for the another object from collection
}
})
// Only change code above this line
return arr;
}
var collection = [{
first: "Romeo",
last: "Montague"
}, {
first: "Mercutio",
last: null
}, {
first: "Tybalt",
last: "Capulet"
}];
var source = {
last: "Capulet"
};
var collectionKeys = [];
for (var i = 0; i < collection.length; i++) {
collectionKeys.push(Object.keys(collection[i]));
}
var sourceKeys = Object.keys(source);
//for every key pair
for (var t = 0; t < collectionKeys.length; t++) {
//for every key in key pair
for (var x = 0; x < collectionKeys[t].length; x++) {
//for every key in search
for (var y = 0; y < sourceKeys.length; y++) {
//see if a key matches
if (sourceKeys[y] == collectionKeys[t][x]) {
if (collection[t][collectionKeys[t][x]] == source[sourceKeys[y]]) {
alert(collection[t].first+ " "+collection[t].last);
} else {
console.log("value not found");
}
} else {
console.log("key not found");
}
}
}
}
Change collection[collectionKeys[t][x]] to collection[t][collectionKeys[t][x]]..collection[collectionKeys[t][x]] gives undefined in console.
This is what I came to on the same problem.
function whereAreYou(collection, source) {
// What's in a name?
// Only change code below this line
var arr = [];
var validObject;
// check each object
for (var each_object in collection ){
validObject = true;
for (var key in source ){
if ( collection[each_object].hasOwnProperty(key)){
if ( collection[each_object][key] != source[key]){
// if no valid key
validObject = false;
}
} else {
// if no valid value
validObject = false;
}
}
// otherwise, give it a green light
if(validObject){
arr.push(collection[each_object]);
}
}
return arr;
}
function whatIsInAName(collection, source) {
const keyCount = Object.keys(source).length;
return collection.filter((item) => {
return Object.entries(item).reduce((acc, [key, value], _, arr) => {
if (keyCount > arr.length) {
acc = false;
} else if (keyCount === arr.length && !source[key]) {
acc = false;
} else if (source[key] && source[key] !== value) {
acc = false;
}
return acc;
}, true)
})
}
I am unable to understand why check corresponding to line if (i in t) - Line no.18 is placed in filter function polyfill :
if (!Array.prototype.filter) {
Array.prototype.filter = function(fun/*, thisArg*/) {
'use strict';
if (this === void 0 || this === null) {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
if (typeof fun !== 'function') {
throw new TypeError();
}
var res = [];
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
for (var i = 0; i < len; i++) {
if (i in t) {
var val = t[i];
// NOTE: Technically this should Object.defineProperty at
// the next index, as push can be affected by
// properties on Object.prototype and Array.prototype.
// But that method's new, and collisions should be
// rare, so use the more-compatible alternative.
if (fun.call(thisArg, val, i, t)) {
res.push(val);
}
}
}
return res;
};
}
It is to avoid the elements which are not defined yet, in the sparse arrays. See the following example,
var array = [];
array[3] = 10;
console.log(array.length);
// 4
So, the length of the array is 4, but only the element at index 3 is defined, all others are not defined yet. So, if you do
for (var i = 0; i < array.length; i += 1) {
console.log(i, array[i]);
}
you will get
0 undefined
1 undefined
2 undefined
3 10
Arrays are special JavaScript objects. The indices are just properties in the array object. Whenever you extend the array object with an index which is not in the array, the length property will be adjusted internally. In this case, there is only one property in the array object, with the name 3 defined. But we are trying to access elements from 0 to 3. So it returns undefined for all the indices which are not present in the array object yet.
To avoid that, we are checking if the current index is really defined in the array object, with that if statement.
for (var i = 0; i < array.length; i += 1) {
if (i in array) {
console.log(i, array[i]);
}
}
would print
3 10
This is because it's possible for JavaScript arrays to have gaps.
For example, consider the following:
var a = ["hello", "howdy", "welcome"];
delete greetings[1];
"0" in a; // true
"1" in a; // false
"2" in a; // true
Array.prototype.myFilter = function(callBack) {
let newArray = [];
for (let i = 0; i < this.length; i++) {
let result = callBack(this[i], i, this);
if (result) {
newArray.push(this[i]);
}
}
return newArray;
IN is a reserved keyword which can be used in for and if statement.
18th line they are doing exist check
Refer this dot vs in.
Create own filter() method
Array.prototype.newFilter = function(func){
let filtered = [], n = this.length;
for(let i = 0; i<n; i++) {
if(func(this[i],i,this))
filtered.push(this[i]);
}
return filtered;
}
let result = [1,2,3,4,5,6,7].newFilter(item => item > 4);
console.log(result);
Array.prototype.myFilter = function(callBackFn) {
let res = [];
if (!Array.isArray(this)) {
throw new Error(`${this} is not a function`);
}
for (let i = 0; i<this.length; i++) {
if (callBackFn(this[i], i, this)) {
res.push(this[i])
}
}
return res
}