I'm trying to rewrite _.each and _.indexOf and it is throwing me for a loop.
My each function takes either an object or an array and passes tests set up.
_['each'] = function(collection, iterator) {
if (Array.isArray(collection) === false && typeof(collection) === 'object') {
var values = Object.values(collection);
var keys = Object.keys(collection);
for (let i = 0; i < values.length; i++) {
iterator(values[i], keys[i], collection, i);
}
} else {
for (let i = 0; i < collection.length; i++) {
iterator(collection[i], i, collection);
}
}
}
So I'm assuming this code is alright since it passes a preset test, but I'm not even sure about that. My question is, how would I write an indexOf() function that also uses the each() function? Each will run a function for every element and won't break, right? And I can't access the index via collection[i] because i is undefined in indexOf's scope. What am I missing?
pseudo code, check if you need the full javascript code :
indexOf = function(element, array){
// found index
index = -1;
// for each element, compare element, store index if found
each array (function(e, i){
// use cmp function to deep compare object/array as values
if(e === element)
{
// remove condition for lastIndexOf
if(index == -1)
{
index = i;
}
}
});
// return found index
return index;
}
Related
I have each() that iterates through a collection and performs a function on it.
_['each'] = function(collection, iterator) {
if (Array.isArray(collection) === false && typeof(collection) ===
'object') {
var values = Object.values(collection);
var keys = Object.keys(collection);
for (let i = 0; i < values.length; i++) {
iterator(values[i], keys[i], collection, i);
}
} else {
for (let i = 0; i < collection.length; i++) {
iterator(collection[i], i, collection);
}
}
}
I want to write map() that takes the results and returns a new array. If I had to use a callback to make use of the each() function, I'm stuck on how to pull those values out. But assuming map() takes two parameters, a collection and the function for the iterator, I'm not sure how I would even access 'i' or any other value as they would be undefined. Should each() have a third parameter to take another callback to adding values into a collection?
Initialize an empty array, call _.each on the input collection, and write results to your output array. Something like
_['map'] = function(collection, iterator) {
var result = [];
var mapIterator = function(value, key, collection, i) {
result[i] = iterator(value, key, collection, i);
}
_['each'](collection, mapIterator);
return result;
}
I've been trying the Number.isInteger() method on chrome console.
and after doing a for loop and checking the result with the console.log(arr); I'm getting an array with only one value of 1. like this [1];
var arr = [1,2,3,'some','string'];
for (var i = 0; i < arr.length; i++) {
if (Number.isInteger(arr[i])) {
arr.splice(arr.indexOf(arr[i], 1));
}
}
Any one have an idea, if I'm doing it wrong or something. thanks for help.
You have a major problem, you are continually removing items from the array while looping through it. You have to go back one step (i--) every time an item is removed.
var arr = [1,2,3,'some','string'];
for (var i = 0; i < arr.length; i++) {
if (!isNaN(arr[i])) { // isNaN return true if it's not a valid number, so we have to inverse the test
arr.splice(i, 1); // if it's a valid number remove the element at the index i (no need to search for the index using indexOf, we already have it its i)
i--; // if we remove an element, we have to go back one step (or we will leave one item behind every time we remove another)
}
}
console.log(arr);
You could use typeof instead:
var arr = [1,2,3,'some','string'];
for (var i = 0; i < arr.length; i++) {
if (typeof arr[i] == 'number') {
arr.splice(arr.indexOf(arr[i], 1));
}
}
The `.splice()' method makes changes to the array itself.
So for each iteration of the array, you are changing it fundamentally.
If you want the array to include only integers:
var arr = [1,2,3,'some','string'];
var newArray = [];
arr.forEach(function(element) {
if (Number.isInteger(element)){
newArray.push(element);
}
});
console.log(newArray);
I am trying to insert data into an array. The data is of object form with 2 properties name and value. I am retrieving the data from a different array object with a completely different structure.
The new array that I am creating is as follow:
newarray = [Object, Object, Object]
where Object = {name: "abc", value: "12"}
This is what I am doing for my purpose:
var newarray = [];
for (var i = 0; i < oldarray.length; i++) {
a = newarray.indexOf(oldarray[i].studentname);
if (a == -1) {
newarray.push({
name: oldarray[i].studentname,
value: oldarray[i].marks
})
}
}
I don't want the student name to repeat so I have tried to use indexOf to check the occurrence of the name in the array. But I am not doing it correctly. What am I doing wrong?
var newarray= [];
for(var i=0; i<oldarray.length; i++)
{
var found = false;
for (var j=0; j<newarray.length; j++) {
if (oldarray[i].studentname == newarray[j].studentname) {
found = true;
break;
}
}
if(!found)
{
newarray.push({
name : oldarray[i].studentname,
value :oldarray[i].marks
});
}
}
This code:
a = newarray.indexOf(oldarray[i].studentname);
if (a == -1)
looks to see if the student's name is an entry in the array. But the entries you're putting in the array aren't just namnes, they're objects.
If you want to find if an object with the same name is in the array, you can use Array#some:
if (!newarray.some(function(entry) { return entry.name == oldarray[i].studentname; })) {
// It's not there, add it
}
Array#some calls its callback for each entry. If the callback returns a truthy value for any entry, some stops and returns true; if the callback never returns a truthy value (or the array is empty), some returns false.
It's a bit more concise with ES2015, if you're using ES2015:
if (!newarray.some(entry => entry.name == oldarray[i].studentname)) {
// It's not there, add it
}
== will require that the names be in the same capitalization, of course. If that may not be true, you might want to make both sides lower (or upper) case first.
I don't see a declared anywhere, make sure you're not falling prey to The Horror of Implicit Globals.
var newarray = [];
for (var i = 0; i < oldarray.length; i++) {
a = findWithAttr(newarray, "studentname", oldarray[i].studentname);
if (a == undefined) {
newarray.push({
name: oldarray[i].studentname,
value: oldarray[i].marks
})
}
}
function findWithAttr(array, attr, value) {
for (var i = 0; i < array.length; i += 1) {
if (array[i][attr] === value) {
return i;
}
}
}
This is what I have tried, it always returns an array with only one value which is the element. I am also not sure if .splice is the correct way to remove the element from the array. Maybe I need a new array and .push only the items i want into it?
function remove(array, element) {
for (var i = 0; i < array.length; i = i + 1) {
if (array[i] === element) {
array = array.splice(i, 1);
}
}
return array;
}
Updated to remove all instances of the element within the array.
function remove(array, element) {
var index = array.indexOf(element)
array.splice(index,1)
if (array.indexOf(element) > -1){
return remove(array,element)
}
return array;
}
I'd like to pull a particular object from an array of objects based on a unique property of that object (ie, a key).
In the following, I'm searching for an element in 'arr' where the key is 8.
var myElement = arr.filter(function(element) {
return element.key === 8;
});
This works, but every time this runs, it will iterate through all elements in the array, even after the correct element has been found. For example, if it finds myElement at index 4, but there are 100 elements in the array, the snippet is running 25x more than it needs to.
Is there a more efficient way, or a way to terminate filter() when it has found myElement?
I feel like I've missed something obvious...
You actually want a find function that returns when the first occurrence is found. You can use Array.prototype.find if you are on ECMAScript 6 spec or you can implement a simple find like:
function find8(arr) {
// type checks skipped for brevity
var i = 0,
j = arr.length;
for (; i < j; i++) {
if (arr[i].key === 8) {
return arr[i];
}
}
throw new Error('Not found');
}
If you want a more generic solution that accepts predicates (i.e. functions that return a boolean value), you can write a more generic function like:
function find(arr, predicate) {
// Again, type checks skipped for brevity
var i = 0,
j = arr.length;
for (; i < j; i++) {
// Note the condition is now a call to the predicate function
if (predicate(arr[i])) {
return arr[i];
}
}
throw new Error('Not found');
}
for (var i=0; i<arr.length; i++) {
if (arr[i].key === 8) {
console.log('FOUND ITEM AT KEY '+8);
break;
}
}
It sounds like you'd be better off with something like this:
function findByKey(items, key) {
for (var index = 0; index < items.length; index++) {
var item = items[index];
if (item.key === key) {
return item;
}
}
return null;
}
Note that if your list is sorted, you can improve upon that by doing a binary-search instead. Though personally I'd suggest hashing (assuming that the same key isn't used twice). Something like:
var array = [/* your data here... */];
var map = {};
//do this once; or once each time your array changes
for (var index = 0; index < array.length; index++) {
var item = array[index];
map[item.key] = item;
}
//now find things by doing this
var itemWithKey8 = map[8];
I'd handle this with a simple for loop (used a function for generic re-use):
function findObject(arr, cond)
for (i = 0; i < arr.length; i++) {
if (cond(arr[i]){
return arr[i];
}
}
}
// now call fincObject(myArray, function(element){element.key === 8})
Or, if you know you are going to do this many times, create a mapping which should be a lot faster:
function makeMapping(arr, keyFunc){
var mapping = {};
for (var i = 0; i < arr.length; i++) {
mapping[keyFunc(arr[i])] = arr[i];
}
return mapping;
}
This returns an object mapping 8 to the object with key.id === 8. Your keyFunc would be:
function keyFunc(element){
return element.key;
}
Why reinvent the wheel? There are a lot of similar answers here, so I'll share a different approach--by far my favorite approach. There is an excellent library called linq.js (both standalone and jQuery plugin versions) which makes searching, filtering, sorting, etc. a breeze.
var myElement = Enumerable.From(arr).FirstOrDefault(null, function(element) {
return element.key === 8;
});
In the above example, the first element that matches the conditions is returned. If nothing is found, null is returned (the first parameter is the default value to return).