What's wrong with this indexOf implementation? - javascript

I know I'm leaving out the -1 option when value can't be found, but why doesn't it work when the value is in the Array? It should be returning 1, but is returning undefined.
function each(collection, callback) {
if (Array.isArray(collection)) {
for (var i = 0; i < collection.length; i++) {
callback(collection[i], i, collection);
}
}
else {
for (var prop in collection) {
callback(collection[prop], prop, collection);
}
}
}
function indexOf(array, value) {
each(array, function(e, index) {
if (e === value) {
return index;
}
})
}
console.log(indexOf([1, 2, 3, 4, 5], 2)); ---->>> undefined;

You're returning the value of index within the callback, not within the indexOf() function itself.
Try this implementation:
function indexOf(array, value) {
var returnVal = -1;
each(array, function(e, index) {
if (e === value) {
returnVal = index;
return false;
}
});
return returnVal;
}
EDIT: As Barmar pointed out, this will return the index of the last occurrence of the element, to return the index of the first occurrence, you'll also have to update each() to be:
function each(collection, callback) {
if (Array.isArray(collection)) {
for (var i = 0; i < collection.length; i++) {
if (callback(collection[i], i, collection) === false) break;
}
}
else {
for (var prop in collection) {
if (callback(collection[prop], prop, collection) === false) break;
}
}
}

function indexOf(array, value) {
each(array, function(e, index) {
if (e === value) {
return index;
}
})
}
The return here is returning from the function that is being passed to each and not from your indexOf function. You need to somehow get that value outside of that function scope to return. With your current implementation of each, you would not be able to break the loop, so you would have to do something like this:
function indexOf(array, value) {
var result = -1;
each(array, function(e, index) {
if (e === value) {
result = index;
}
})
return result;
}
http://jsfiddle.net/efzogzxq/

Your indexOf() function isn't returning anything. The callback returns the index if found, but you're not doing anything with that value. You could do this:
for (var i = 0; i < collection.length; i++) {
var result= callback(collection[i], i, collection);
if (result) return result;
}

You'll want to return the index if found. Also, you need to exit the loop once you've found the value, otherwise you're doing a lastIndexOf instead of indexOf.
function each(collection, callback) {
if (Array.isArray(collection)) {
for (var i = 0; i < collection.length; i++) {
if (callback(collection[i], i, collection) === true) {
return;
}
}
}
else {
for (var prop in collection) {
if (callback(collection[prop], prop, collection) === true) {
return;
}
}
}
}
function indexOf(array, value) {
var foundIndex = -1;
each(array, function(e, index) {
if (e === value) {
foundIndex = index;
return true;
}
});
return foundIndex;
}

The indexOf function is always returning undefined because the you are only returning a value from the callback function, not the indexOf function.
Try this:
function indexOf(array, value) {
var i;
each(array, function(e, index) {
if (e === value) {
i = index;
}
})
return i;
}

Related

JavaScript For Loop does not return

My problem is that a loop that is called in another for loop and should return an object does not return anything. When I set a breakpoint to the return statement the object is there but undefined in my callback function. types object is a global object that contains many objects as properties with the properties "title" and "id".
function searchObj(obj, query) {
for (var key in obj) {
var value = obj[key];
if (typeof value === 'object') {
searchObj(value, query)
}
if (value === query) {
return obj;
}
}
}
The function is called from here:
function callback(data){
var logs = [];
var results = data.d.results;
for (var i = 0; results.length; i++) {
var item = results[i];
var action = util.searchObj(types, item.ActionId);
var obj = {
'Created': item.Created,
'Text': String.format(action.title, item.Author.Title),
'Author': item.Author
}
logs.push(obj);
}
console.log(logs);
}
Try this:
function searchObj(obj, query) {
for (var key in obj) {
var value = obj[key];
if (typeof value === 'object') {
var result = searchObj(value, query);
if (result) {
return result;
}
}
if (value === query) {
return obj;
}
}
}

Remove elements from inside Array prototype

I'm trying to extend js native array inside angular service to add some extra features without prototyping global objects.
app.factory('Collection', function($http, $q) {
var Collection = function(arr) {
this.key = 'id';
this._last = 0;
this._first = 77777777; //just big number.
this.append(arr);
}
Collection.prototype = new Array;
Collection.prototype.orderBy = function(n, reverse) {
if (reverse) {
this.sort(function(a, b) {
return b[n] - a[n];
})
} else {
this.sort(function(a, b) {
return a[n] - b[n];
})
}
}
Collection.prototype.spliceBy = function(key, val) {
for (var i = 0; i < this.length; i++) {
if (this[i][key] !== val) {
this.splice(i, 1); ///THIS NEVER HAPPENS !!
console.log('removed ' + i + ' from ', this);
}
}
}
Collection.prototype.subset = function(key, val) {
return this.filter(function(v) {
return (v[key] === val);
});
}
Collection.prototype.add = function(obj) {
for (var i = 0; i < this.length; i++) {
if (this[i][this.key] > this._last) {
this._last = this[i][this.key];
}
if (this[i][this.key] < this._first) {
this._first = this[i][this.key];
}
if (this[i][this.key] === data[this.key]) {
if (override) {
this[i] = data;
console.log('updated uniquePush');
}
return i;
break;
}
}
var id = this.push(data) - 1;
data._index = id;
return id;
}
return collection
});
This is working fine except for the spliceBy function.
I need to filter out elements that does not have value = x;
For example in my controller
.controller(function($scope,Collection){
$scope.posts = new Collection;
$scope.posts.add({id:1,type:'post'});
$scope.posts.add({id:2,type:'comment'});
//Collection is now [{id:1,type:post},{id:2,type:comment}];
//i want to remove all comments from array
$scope.posts.spliceBy('type','comment');
});
Yet nothing happens when calling spliceBy :*(
The spliceBy function will not work if you have two elements to remove in a row, because splice is updating the indexes from i to array.length. Try this instead:
Collection.prototype.spliceBy = function(key, val) {
var i = this.length;
while (i--) {
if (this[i][key] !== val) {
this.splice(i, 1); ///THIS NEVER HAPPENS !!
console.log('removed ' + i + ' from ', this);
}
}
}

Improvising Code Into A More DRY Approach?

I am formatting an array in the function inputCategories, and am unable to correctly add a third argument of "category" - forcing me replicate the function multiple times.
Here is the current state:
Calling the function with arguments.
$scope.categories = inputCategories($scope.item.categories, $scope.settings.categories);
function inputCategories (input, settings) {
var updatedSettings = [];
angular.forEach(input, function(obj) {
updatedSettings.push({"category": obj, "ticked": true});
});
var list = updatedSettings.concat(settings);
list.sort(function(a, b) {
return (a.category > b.category) - (a.category < b.category);
});
for ( var i = 1; i < list.length; i++ ){
if(list[i-1].category == list[i].category) {
list.splice(i,1);
}
}
return list;
};
Here are the places which would require a third argument of "category".
function inputCategories (input, settings) {
var updatedSettings = [];
angular.forEach(input, function(obj) {
updatedSettings.push({****"category"****: obj, "ticked": true});
});
var list = updatedSettings.concat(settings);
list.sort(function(a, b) {
return (a.****category**** > b.****category****) - (a.****category**** < b.****category****);
});
for ( var i = 1; i < list.length; i++ ){
if(list[i-1].****category**** == list[i].****category****) {
list.splice(i,1);
}
}
return list;
};
I think that the issue I am having is because I am mixing up strings and a variable that is a string, inside of the object on the fourth line...?
Perhaps you could do something like this:
function inputCategories (input, settings, category) {
var updatedSettings = [];
angular.forEach(input, function(obj) {
var setting = { "ticked": true };
setting[category] = obj;
updatedSettings.push(setting);
});
var list = updatedSettings.concat(settings);
list.sort(function(a, b) {
return (a[category] > b[category]) - (a[category] < b[category]);
});
for ( var i = 1; i < list.length; i++ ){
if(list[i-1][category] == list[i][category]) {
list.splice(i,1);
}
}
return list;
};

javascript callbacks in for loop

I have a for loop in a function in the structure
func(var, callback) {
for(i = 0; i < len; i++) {
validate(var, function(value) {
if (!value) { callback(value) }
}
}
callback(true);
}
Where the function validate returns a boolean. I would only like to call my callback with true if it has not been called before. I tried putting a return after callback(value) but that didn't help.
Set a flag:
function func(foo, callback) {
var called = false;
for(var i = 0; i < len; i++) {
validate(foo, function(value) {
if (!value) {
called = true;
callback(value);
}
})
}
if (!called) {
callback(true);
}
}

Search array for string (javascript+angularjs)

I want to check if an array contains a string and followup on it. indexOf() is not an option because it is strict.
Plunker Here
You can find the described problem in the app.filter('myOtherFilter', function()
app.filter('myOtherFilter', function() {
return function(data, values) {
var vs = [];
angular.forEach(values, function(item){
if(!!item.truth){
vs.push(item.value);
}
});
if(vs.length === 0) return data;
var result = [];
angular.forEach(data, function(item){
if(vs.toString().search(item.name) >= 0) {
result.push(item);
}
});
return result;
}
});
Is this correct and is the error somewhere else?
angular.forEach(data, function(item){
for(var i = 0; i < vs.length; i++){
if(item.name.search(vs[i]) >= 0) {
result.push(item);
}
}
});
You could always extract the Angular filter filter, which takes an array but will handle the different types properly. Here is the general idea:
app.filter('filter', function($filter) {
var filterFilter = $filter('filter');
function find(item, query) {
return filterFilter([item], query).length > 0;
}
return function(data, values) {
var result = [];
angular.forEach(data, function(item) {
for(var i = 0; i < values.length; i++) {
if(find(item, values[i])) {
result.push(item);
break;
}
}
});
return result;
};
}
});
You'll have to change the structure of the data you are passing in. Pass in a list of values, not a list of {truth: true}. This solution allows you to leverage the existing power of the Angular "filter" filter.

Categories

Resources