Zepto's use of array.filter - javascript

I am trying to boost my Javascript understanding, so I've been looking through the Zepto library. I came across this line:
uniq = function(array){
return array.filter(function(item, idx){
return array.indexOf(item) == idx
})
}
What is the purpose of this function? From what I can tell, it is creating a new, unique array of elements, right? But isn't it essentially just cloning the array? If so, wouldn't array.slice() be faster?
Finally, wouldn't it increase performance to change array.indexOf(item) to array.indexOf(item,idx)? Or better yet, just return true? When does array.indexOf(item)==idx not equal true? Is this to prevent duplicate items? But when would that ever actually happen?

it is creating a new, unique array of elements, right?
It just filter your array elements to return unique elements.
demo
But isn't it essentially just cloning the array?
No as I explain above.
If so, wouldn't array.slice() be faster?
Slice doesn't remove duplicates.
Finally, wouldn't it increase performance to change array.indexOf(item) to array.indexOf(item,idx)? Or better yet, just return true?
If you only return true you won't identify if the element is duplicated or not.
demo
When does array.indexOf(item)==idx not equal true?
Example:
I have the following array:
['10', '20', '30', '20', '10']
Iterations:
1: array.IndexOf(10) == 0 ? // yes, so return true
2: array.IndexOf(20) == 1 ? // yes, so return true
3: array.IndexOf(30) == 2 ? // yes, so return true
4: array.IndexOf(20) == 3 ? // no because array.indexOf(20) is 1 , so return false
5: array.IndexOf(10) == 4 ? // no because array.indexOf(10) is 2 , so return false
So, when the element has already been found it gets false because the indexes are not the same.

It seems that this code is eliminating duplicates.

Ahh I see the "difference" we're questioning. You kinda answered it though, in your edit. I think this method returns a new array that contains unique values from the original.
When the indexOf method scans the array, it finds the first occurrence of the currently inspected item. If that occurrence is not the same index as the current index being inspected, the indexOf result will not equal idx. Therefore, it will not return the value because it either wasn't found, or it was found earlier in the array (which means it's a duplicate).
Here's an example:
[10, 30, 10, 100]
When the filter methods goes through the items: 10, 30, 10, then 100, it will perform the indexOf on it.
For 10, indexOf will return 0. And idx is also 0.
For 30, indexOf will return 1. And idx is also 1.
For 10, indexOf will return 0. But idx will be 2.
For 100, indexOf will return 3. And idx is also 3.
Therefore, [10, 30, 100] will be returned, not just a simple clone of the original.

The function (as named) takes unique items in the original array, and returns them in a new array. So if the original array has distinct items, it will just return a clone.
Remember, indexOf returns the first index of a given item. So if the current item appears twice in the array, the filter value will be false.
For example.
var a = [1, 2, 3];
var b = [1, 2, 3, 2];
console.log(uniq(a)); // [1,2,3]
console.log(uniq(b)); // [1,2,3]
​

As others have said already this gets rid of duplicates in an array. Just added my answer to show why this works. If you log out the values inside the filter function you'll see the pattern:
array.filter(function( item, idx ){
console.log( item, idx, array.indexOf( item ) );
...
...
console.log( uniq( ['a','a','b','b','c','c','c','d'] ) );
/*
a 0 0 *
a 1 0
b 2 2 *
b 3 2
c 4 4 *
c 5 4
c 6 4
d 7 7 *
*/
Check the last column, that's what determines if the item already exists so the comparison array.indexOf(item) == idx checks against the second column, if it's not the same number it is a duplicate. For the item to be unique (*) the index and the index position must match.

Related

Find a unique number in an array

Can someone explain to me what exactly is this line of code doing?
function findUniq(array) {
return array.find((item) => array.indexOf(item) === array.lastIndexOf(item))
}
I want to know what this line is exactly doing:
return array.find((item) => array.indexOf(item) === array.lastIndexOf(item))
What I think is happening here is that for every item inside the array it is comparing the first index of that item to the last index of item. it returns the items that equal to eachother.
I don't understand how it is returning the unique array.
If I were to write this function it would be like this:
return array.find((item) => array.indexOf(item) != array.lastIndexOf(item))
However, that doesn't work since it is returning the common number.
thanks
The Array.prototype.find() method takes a predicate function (a function that returns true or false based on input parameters) and then returns the element provided the predicate is true.
In your case, given the scenario:
var array1 = [1,2,1,3,5,2,3];
var array2 = [1,2,1,3,5,5,3];
function findUnique(array){
return array.find(
// The code below runs for every element of the array.
// - for each element, it takes the element and checks if first position, is the same as last position in the array
(item) => array.indexOf(item) === array.lastIndexOf(item) //
);
}
console.log("For array 1, unique item is: ",findUnique(array1));
console.log("For array 2, unique item is: ",findUnique(array2));
Array.prototype.indexOf(yourArrayElement) returns the position of the first occurrence of yourArrayElement
Oh the other hand, Array.prototype.lastIndexOf(yourArrayElement) returns the position of the last occurrence of yourArrayElement
If yourArrayElement only exists once within the array, the first and last position will be the same and Array.prototype.find() will return that element.
indexOf returns the first found position of the given element in the array whereas lastIndexOf returns the latest position of the given element in the array.
If indexOf === lastIndexOf, it means the first found element is the same as the latest one --> the element is unique in the array.
The reason that it return a unique number, because the find function loop from the start and from the end on each iteration, so if the index of both indexOf that loops from the start and lastIndexOf that loops from the end is equal it's mean that there are no duplicates across the array beside the current item.
This can be written two ways:
Match the first occurrence index with the final occurrence index.
const data = ['a', 'b', 'c', 'b', 'a'];
const findUniq = arr =>
arr.find((item) => arr.indexOf(item) === arr.lastIndexOf(item));
console.log(findUniq(data)); // 'c'
Check for an index of -1 for the next indexOf check.
const data = ['a', 'b', 'c', 'b', 'a'];
const findUniq = arr =>
arr.find((item) => arr.indexOf(item, arr.indexOf(item) + 1) === -1);
console.log(findUniq(data)); // 'c'
The first one is easier to read and convey, because it it written to compare the first and last index check. The second one is more obscure, but both work.

.filter in double arrays

Helo. I am beginner in this JavaScript journey, and i just hit a wall. I am learning .filter() function on Array. My exercise is:
Return only the rows in the matrix that have all positive integers
I have no problems with single arrays. For example my code with single array:
function positiveRowsOnly (array) {
var result = array.filter(function (ind) {
return ind < 0;
});
return result;
};
console.log(positiveRowsOnly([1, 10, -100, 2, -20, 200, 3, 30, 300]));
its quite simple for me to understand that "ind" in .filter will accept every index from given array and check if ind < 0.
What i am struggling is if i have double arrays. Original exercise is with double arrays.
function positiveRowsOnly (array) {
var result = array.filter(function (ind) {
return ind < 0;
});
return result;
};
console.log(positiveRowsOnly([[1, 10, -100 ], [ 2, -20, 200 ], [ 3, 30, 300 ]]));
On internet i just can not find any deeper meaning how .filter() works: does filter go in one array and gets each index? Does "ind" gets just the first array and not first array index? I was looking at Math functions or indexOf but no luck. I hope you understand my struggle. Can anyone explain how this can be done or most important, how does .filter work in double arrays?
In pseudo code i know, look in array index, if it has a negative number than ignore, else return that array.
The function filter will work the same way regardless of the type of every element within an array. Basically, will loop and test a condition to filter that array.
So, you can use the function every to loop (nested arrays) and check the values from them.
+----- This is the nested array
|
| +----- This is the function 'every' that will loop
| | and check every element.
v v
ind.every(function(n) { -+
return n >= 0 |----> This is the predicate.
}); ^ -+
|
|
+----- This is the test to check every
element within the nested array.
The function every returns true when every element within an array meets a specific predicate (handler, callback, Etc,.), in your case n >= 0.
function positiveRowsOnly(array) {
var result = array.filter(function(ind) {
return ind.every(function(n) {
return n >= 0
});
});
return result;
};
console.log(positiveRowsOnly([
[1, 10, -100],
[2, -20, 200],
[3, 30, 300]
]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
On internet i just can not find any deeper meaning how .filter() works: does filter go in one array and gets each index? Does "ind" gets just the first array and not first array index?
The Array.prototype.filter function will loop over the elements of an array, and pass each element to the callback function you provide (sometimes referred to as a 'predicate'). The index is actually the second argument to your predicate function.
In your case, each element of your outermost array is also an array. So your filter predicate will be passed each of the inner arrays, one at a time. If you want to filter the inner arrays, you'll need to "loop inside your loop", and call the filter function again to find the positive numbers.
Since you only want to return entire positive rows, you may want to look at other functions like Array.prototype.every, which will return a boolean if the predicate function matches. Then you could return the whole, unfiltered row.
A good trick is to try putting a console.log(ind) inside your filter functions so you can see what each element of that array looks like.

Shorter way to remove an item by index from an array [duplicate]

This question already has answers here:
How to delete an item from state array?
(18 answers)
Closed 5 years ago.
I've made this post last year and today, I assume things can be simplified.
I need to remove an item from an array but by the index. When by the index, it does not matter if the array has same values. Your typical example:
let arr = [1,2,3,2,1] // just an array, not array with objects
let x = 1;
// This will not be an expected result:
// Find all values that is equal to 1 then remove
arr.filter(num => num !== x) //=> [2,3,2]
My expectation is when I remove the last element (1), for example, the array should be [1,2,3,2]:
let index = 4; // which is the last "1" in the array
let indexVal = arr.indexOf(4) // 1
let newArray = arr.splice(indexVal, 1) //=> [1,2,3,2]
Now, it's 2017, almost '18, is there a shorter way (es5/6) of doing this without any polyfil?
Edit:
Think of this as a todo:
<ul>
<li>me</li>
<li>me</li> // click to delete this one
<li>you</li>
<li>me</li>
</ul>
To correctly remove that item, I have to delete by the index not value
The Array.filter callback gives 2 arguments, number and index and you can filter the array this way.
let arr = [1,2,3,2,1]
let x = 4; //suppose you want to remove element at 4th index
let editedArray = arr.filter((num, index) => index !== x) //editedArray = [1,2,3,2]
EDIT:
The third parameter gives the whole array. Thanks #Oliver for pointing this out in comment
arr.splice(index, 1);
or if you specifically want to remove the last element:
arr.pop();
No indexOf call. The indexOf call never should have been there; it only ever looked like it worked because indexOf returns -1 for an element that isn't present, and splice treats negative indices as counting from the end of the array.
Also, splice modifies the array in place and returns an array of removed elements, so assigning its return value the way you were doing is misleading.
The only way I can think of is the one we use in Redux every day:
const arr = [1, 2, 3, 2, 1]
const index = 4 // index of the item you want to remove
const newArr = [...arr.slice(0, index), ...arr.slice(index + 1)]
console.log(newArr) // [1, 2, 3, 2]
It might not be the shortest but it is more 2017 and it is immutable, which is very important!
Ajay's answer might be what you're looking for. Anyway, there are people like me who prefer slightly-more-lines-but-more-readable/rewritable/maintable solution, I'd do it this way:
function removeElementByIndex(arr, x) {
var newArr = [];
for(var i = 0; i < arr.length; i++) {
if(i != x) {
newArr.push(arr[i]);
}
}
return newArr;
}
// Usage
removeElementByIndex([1, 2, 3, 2, 1], 4);// outputs: [1, 2, 3, 2]
Now, it's 2017, almost '18, is there a shorter way (es5/6) of doing
this without any polyfil?
LOL! Many basic things not yet implemented. We'll have to wait for 2118 or another programming language to replace JS (oh wait, there's one, aka jQuery :P ).

Difference of Two Arrays

I have a quick question about finding the difference between two arrays. I found a chunk of JavaScript code that does what I want here and modified it a bit:
JS
function diffArray(arr1, arr2) {
var newArr = [];
var myArr = arr1.concat(arr2);
newArr = myArr.filter(function(item){
return arr2.indexOf(item) < 0 || arr1.indexOf(item) < 0;
});
return newArr;
}
diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]);
However, I'm not sure that I really understand what the filter function is doing here. Could anyone give me a clear explanation of how this works.
Note: I already know the basics of filter functions. I'm looking for a specfic explanation of this filter.
newArr = myArr.filter(function(item){
return arr2.indexOf(item) < 0 || arr1.indexOf(item) < 0;
});
The Array#filter function iterates over each element of the newly made array from concat function (containing every element from arr1 and arr2 arrays).
If at least one of the conditions arr2.indexOf(item) < 0 or arr1.indexOf(item) < 0 is fulfilled (returns true) at some iteration, the Array#filter function filters out (returns) that specified (actually iterated) element.
In case, if the iterated element is both in arr1 and arr2, the indexOf function will return it's index (which is higher than 0) - the condition will return false (it's not smaller than 0). We will receive then false || false which is false and that element won't be included in the newArr array, containing unique elements.
In case, if given element is only in one array (it doesn't exist in the second one - it's index will return -1 - it will fulfill the < 0 condition) - one of the conditions will return true, so the both conditions will become true || false which is true - given element will be included in the newArr array.
Filter is using a lambda to filter the list.
This essentially means that it executes the code between the { } and returns an array that meets the criteria. Your criteria are that the index of the item is greater than 0 in both arrays which is essentially just saying it's in both arrays.
you can read more here: https://www.w3schools.com/jsref/jsref_filter.asp
Easy way of thinking this is that filter function is internally creating an array.
The elements of this internal array are the ones that pass the condition in the callback function. This is possible since this decision is being made one element at a time in the callback function.
Condition arr2.indexOf(item) < 0 || arr1.indexOf(item) < 0 passes only for element 4 since at that time 4 is not present inside arr1. Since true is returned element 4 is added in the internal array. Ultimately 4 is returned in newArr.

Empty elements in JS array declaration

it's my first question here, after have been reading for years, so be nice with me please.
I'm having trouble with array management in js/jq.
I have array with several elements, which is processed with $.each function.
I want to extract matching elements to another array and return this array.
But for some reason (don't know if it's because array declaration, jquery.each function...) I'm having first empty element.
I think I'm making this more difficult to understand than it's, so made jsfiddle.
var arr = new Array();
$.each([1,2,3], function(index,element){
if (element == 2){
arr[index] = element;
}
});
arr must have only 1 element, but arr.length returns 2 because first array slot is empty.
Here is a fiddle http://jsfiddle.net/moay7y95/
I'm so sure that it's a simple and little stupid thing, but I've not been able to find an answer.
Thanks in advance!
You are pushing an element in array at 1st index. So, javascript by default put undefined at 0th index.
So, at the end of each your array will be
[undefined, 2];
This is the reason you get the length as 2.
You can use push to add element in array:
$.each([1, 2, 3], function (index, element) {
if (element == 2) {
arr.push(element);
elem++;
}
});
Demo: https://jsfiddle.net/tusharj/moay7y95/2/
The push() method adds one or more elements to the end of an array and returns the new length of the array.
Docs: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/push
Best solution: Use .filter
var arr = [1, 2, 3].filter(function(value){
return value == 2
});
If the return value is true, the element is included in the returned array, otherwise it is ignored. This is pure js, so you don't need jquery. See documentation about .filter
Your problem: Use .push
Try using the .push method on the array (see Mozilla's documentation).
var arr = new Array();
$.each([1,2,3], function(index,element){
if (element == 2){
arr.push(element);
}
});
The .push method will dynamically grow your array as elements are added.
Your problem is that the value of index inside your function is a reference point in your initial array. This means that if you look at the returned array, you will find [undefined, 2] and so the length is returning as 2. If your condition were element == 3 you would have two empty slots.
Alternatively: Use .grep
Another jQuery method is $.grep. See http://api.jquery.com/jquery.grep/
var arr = $.grep([1, 2, 3], function (value) {
return value == 2
});
This is jQuery's implementation of javascript's .filter.
Your array [1, 2, 3] has three elements, and three associated indexes: 0, 1 and 2. In each iteration of your $.each() loop over that array, the index and its value get passed to the function.
So the first call gets 0, 1 passed as its arguments. The second call gets 1, 2 passed as its arguments, then the if statement condition evaluates to true, so your code:
arr[index] = element;
is actually equivalent to
arr[1] = 2;
Since you're inserting an element at index 1, you'll end up with an empty index 0.
You could instead simply use Array.push() to add the element to the array:
arr.push(element);
As already mentioned the problem is you are assigning the value at index 1, so the array will be considered of length 2.
One solution is to use .push() instead of assigning based on index.
Another approach could be is to use Array.filter() and return true if the element matches the conditon
var arr = [1, 2, 3].filter(function(value){
return value == 2
});
Demo: Fiddle
Using $.grep()
var arr = $.grep([1, 2, 3], function (value) {
return value == 2
});
[1, 2, 3]
if (element == 2)
arr[index] = element;
This means you're setting arr[1] to element.
If you want to add it to the arr sequentially (filling it up normally), push it into art
arr.push(element);
Seems like you're looking for Array.filter (MDN).
var elems = ["car", "bike"];
var arr = [1, 2, 3, "car", 5, "bike"].filter(myFilterFunc);
function myFilterFunc(val) {
if (elems.indexOf(val) > -1) return true;
}
Fiddle

Categories

Resources