Javascript filter function polyfill - javascript

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
}

Related

Find the length of an specific object within an array

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.

Searching for matching property and value pairs in an array of objects

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)
})
}

Creating a frequency listing of characters

charFreq function that's not quite working out. Hit a wall. I know I may need to
do a conditional. Calling the function returns an Object error. I'm attempting
to get string into an empty object displaying the characters like this - Object
{o: 4, p: 5, z: 2, w: 4, y: 1…}. New to Javascript by the way.
Just realized I shouldn't be appending anything. Do I need to do a .push() to
push the array into the object?
function charFreq (string){
var emptyObj = {};
for(var i = 0; i < string.length; i++) {
// console.log(string.charAt(i));
var args = [string.charAt(i)];
var emptyArr = [''].concat(args);
emptyObj += emptyArr
}
return emptyObj
}
undefined
charFreq('alkdjflkajdsf')
"[object Object],a,l,k,d,j,f,l,k,a,j,d,s,f"
You just need to set emptyObj's key of that specific letter to either 1 if it doesn't exist or increment the count if it already does.
function charFreq(string) {
var obj = {};
for (var i = 0; i < string.length; i++) {
if (!obj.hasOwnProperty(string[i])) {
obj[string[i]] = 1;
} else {
obj[string[i]]++;
}
}
return obj;
}
console.log(charFreq('alkdjflkajdsf'));
Try this instead: you need to create an object property first, then increment it. What you do, is implicitly convert the object to a string and concatenate more string data to it (using += and concat).
This is a simple approach:
function charFreq(string){
var emptyObj={};
for(var i=0; i<string.length; i++) {
if(!emptyObj.hasOwnProperty(string[i])){ // if property doesn’t exist
emptyObj[string[i]]=0; // create it and set to 0
}
emptyObj[string[i]]++; // increment it
}
return emptyObj;
}
A modified version of Richard Kho's code:
function charFreq(string) {
var obj = {};
for (var i = 0; i < string.length; i++) {
var c=string[i];
if (c=='') continue;
if (obj[c]==null) obj[c]=0;
obj[c]++;
}
return obj;
}

Create an array and check against it

I am not sure of how to do this, but what I want to do it create an array and be able to add new items to this array. Since the items are supposed to be a random number, when a new instance is created I would like it to be checked against the rest of the array and be sure that the number it has generated is not already in the array. How would I accomplish something like this?
I looked at Šime Vidas's answer and it seems to work, but I tried to shorten it to
var arr = [];
function add(a) {
var n = ~~(Math.random() * 100);
for (var i = 0; i < a.length; i++) {
if ( a[i] === n) { a.push(n) }
}
}
for (var i=0; i<5; i++){
add(arr)
}
document.getElementById('output').innerHTML += arr;
and I don't understand why this wouldn't work. It does pretty much the same thing, correct?
var arr = [];
function add(a) {
var n = ~~(Math.random() * 1000);
!is(a, n) && a.push(n);
}
function is(a, n) {
for (var i = 0; i < a.length; i++) {
if ( a[i] === n ) { return true; }
}
return false;
}
The add function creates a random integer number between 0 and 1000, and adds it to the array.
The is function checks whether the n number is somewhere inside the a array.
Demo: http://jsfiddle.net/kHhMp/2/
Demo 2: http://jsfiddle.net/kHhMp/3/
(Demo 2 shows that a number will only be added to the array if it's not already in it.)
btw
!is(a, n) && a.push(n);
is a short form of this:
if ( is(a, n) == false ) { a.push(n); }
The number is added to the array only if is(a, n) returns false.
UPDATE
var arr = [];
function add(a) {
var n = ~~(Math.random() * 1000),
ok = true;
for (var i = 0; i < a.length; i++) {
if ( a[i] === n ) { ok = false; }
}
ok && a.push(n);
}
If you enjoy fast code and you have many items in your array, you should use an Object rather than an Array.
Instead of doing var my_array=[]; my_array.push(my_number), use var my_object = {}; my_object[my_number] = true to add items in your structure.
With that approach, you can easily check if a new number is already in there with an if (my_object[my_number]) { /* already there */ } else { /* not there yet */ }
Once you're done, you can extract the list of numbers as an array by either using var keys = Object.keys(my_object), or if that's not available, var keys=[],i=0;for (keys[i++] in my_object);, more or less.
You may extend the built in Array object for your needs.
Array.prototype.pushUnique = function(value) {
var len = this.length;
for(var i = 0; i < len; i++) {
if(this[i]===value) return;
}
this.push(value);
}
var uniques = new Array();
uniques.pushUnique(1);
uniques.pushUnique(2);
uniques.pushUnique(1);
// array will contain only 1 and 2
The fastest, most cross-browser way is to iterate over the array using a loop:
var arr = [];
function addNum(num) {
for (i = 0, len = arr.length; i < len; i++) {
if ( arr[i] === num ) { return false; }
}
arr.push(num);
}
Be sure to get the length of the array before you run the loop so the length property isn't constantly checked.
var array = []
array[0] = 'Item'
array[0] === undefined
# returns false
array[1] === undefined
# returns true

replace items which appear in both arrays in javascript

I have two arrays and want to remove from one all elements which exist in the other as well.
Can this be done with native JS?
Is there a jQuery function to do it?
What are best practices to do so (the
faster the better)
p.s.: just post code in other languages too, maybe I can port it to Javascript
Update, after accepting answer to help the JS dudes ;-)
// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
// Array Contains - By Helmuth Lammer (TU Vienna)
Array.prototype.contains = function(key){
for(var i = 0; i<this.length; i++){
if(this[i] == key) return i;
}
return false;
}
(There is a native JS method named contains too, but it should work)
Given two sets a and b, to remove all elements present in a from b. Translation to array formatting and javascript left as an exercise to the reader. Optimizations exist for sorted arrays.
for(element e : b) {
if(a.contains(e)) {
b.remove(e);
}
}
var foo = [1, 2, 3, 4, 5];
function removeAll(a, b) {
var i = 0,
j;
while (i < b.length) {
for (j = 0; j < a.length; j++) {
if (a[j] === b[i]) {
b.splice(i, 1);
i--;
break;
}
}
i++;
}
}
removeAll([2, 4], foo);
// foo === [1, 3, 5]
[See it in action]
// two arrays
var arr1 = [1,2,3,4,5];
var arr2 = [4,5,6,7,8];
// returns the element's index in an array
// or -1 if there isn't such an element
function indexOf( arr, el ) {
for ( var i = arr.length; i--; ) {
if ( arr[i] === el ) return i;
}
return -1;
}
// go through array1 and remove all
// elements which is also in array2
​for ( var i = arr1.length; i--; )​ {
if ( indexOf( arr2, arr1[i] ) !== -1 ) {
arr1.splice(i, 1);
}
}
// result should be: arr1 = [1,2,3]
alert( arr1 );
​
I had to write a code once where an Ajax call would return an array containing a list of element which must be deleted from a local (client-side) array.
I ended implementing it this way:
// The filter method creates a new array with all elements that pass the
// test implemented by the provided function.
local_array = local_array.filter( function (element) {
// Now I check if this element is present in the "delete" array. In order to do
// that I use the "some" method which runs a callback function for each of the
// array elements and returns true or false.
return !items_to_delete.some( function(item) {
return item.pk === element.pk;
});
});
Edit:
In order to make it cross-browser (I am talking about IE here). You will have to define the some and filter functions. Just put this somewhere in your code:
if (!Array.prototype.some)
{
Array.prototype.some = function(fun /*, thisp*/)
{
var i = 0,
len = this.length >>> 0;
if (typeof fun != "function")
throw new TypeError();
var thisp = arguments[1];
for (; i < len; i++)
{
if (i in this &&
fun.call(thisp, this[i], i, this))
return true;
}
return false;
};
}
if (!Array.prototype.filter)
{
Array.prototype.filter = function(fun /*, thisp*/)
{
var len = this.length >>> 0;
if (typeof fun != "function")
throw new TypeError();
var res = [];
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in this)
{
var val = this[i]; // in case fun mutates this
if (fun.call(thisp, val, i, this))
res.push(val);
}
}
return res;
};
}

Categories