How to implement the equivalent for jQuery .map() in plain JS? - javascript

I'm working to convert my code into plain JS, it's really hard. I need a function to get the real scroll container and the one I have goes like this, using .map().
//get true container for scroll events
function getScrollContainer(c) {
return $(c).map(function() {
var cnt = this,
isWin = !cnt.nodeName || $.inArray( cnt.nodeName.toLowerCase(), ['iframe','#document','html','body'] ) != -1;
if (!isWin) return cnt;
var doc = (cnt.contentWindow || cnt).document || cnt.ownerDocument || cnt;
return /webkit/i.test(navigator.userAgent) || doc.compatMode == 'BackCompat' ?
doc.body :
doc.documentElement;
});
}
console.log(getScrollContainer(window));
is there a way to accomplish this?

Map is just a fancy way to say "apply this function to every element in the list" so it could be easily implemented in a for loop.
Something like this: (Not perfect or tested, but should give you an idea)
function getScrollContainer(c) {
c = Object.prototype.toString.call( c ) === '[object Array] ? c : [c];
for (var i = 0; i < c.length; i++) {
var cnt = c[i],
isWin = !cnt.nodeName || indexOf(cnt.nodeName.toLowerCase(), ['iframe','#document','html','body'] ) != -1;
if (!isWin) return cnt;
var doc = (cnt.contentWindow || cnt).document || cnt.ownerDocument || cnt;
return /webkit/i.test(navigator.userAgent) || doc.compatMode == 'BackCompat' ?
doc.body :
doc.documentElement;
}
}

Based on the code that you have posted you don't need to use the map method. Your function accepts just one parameter so you can just remove the var cnt = this, line and use the c parameter. Now your function instead of returning a jQuery-wrapped array with length of 1, returns a HTMLElement object.
If you want to pass an array to the function you can use Array.prototype.map method:
function getScrollContainer(c) {
return c.map(function(value, index, arr) {
// ...
});
}
console.log(getScrollConainer([window, 'foo']));
And for replacing the jQuery $.inArray utility function you can use the Array.prototype.indexOf method:
['iframe','#document','html','body'].indexOf(cnt.nodeName.toLowerCase()) != -1
Note that IE8 and below do no support the Array map and indexOf methods. If you want to support those browsers you can use a polyfill. MDN suggests this polyfill for Array.prototype.indexOf.

While the answers above will work in your case, they will not in every case. jQuery's map creates a new array and returns that array after applying the function to every item in the array.
the prototype map function does not create a new array and instead simply applies the function to each item.
this sometime will produce the same result and others will produce different results.
You will get the same result for this example:
$.map([1,2,3], function(i){ return i+1; });
=> [2,3,4]
[1,2,3].map(function(i){ return i+1; });
=> [2,3,4]
However the below example will provide a different result:
$.map([1,2,3], function(i){ if(i > 1){ return i; } });
=> [2,3]
[1,2,3].map(function(i){ if(i > 1){ return i; } });
=> [undefined,2,3]

In case anyone is interested, you can use if statements inside .map() if you create a new function for the Array prototype:
Array.prototype.custom_Map = function(x) {
arr = [];
for (var i = 0; i < this.length; i++)
var _this = (x(this[i], i, this));
if(_this !== null) {
arr.push(_this);
return arr;
};
You can use it the following way:
x.custom_Map(function(a){ if(a>0) {return true;}});

Related

Efficient way to compare arrays in javascript

I need to compare the elements from two arrays as follows:
arr1[0] ? arr2[0]
arr1[1] ? arr2[1]
arr1[2] ? arr2[2]
etc.
I wrote some code but it seems to be slow when I try to compare 1000 objects like this on each array :
{
"id":"event707",
"name":"Custom707",
"type":"disabled",
"default_metric":false,
"participation":"disabled",
"serialization":"always_record"
}
This is how my function looks like (just an example for two arrays with hard coded data).
function compare() {
var step = 0;
var fruits1 = [{"apple":25},{"bannana":36},{"orange":6}];
var fruits2 = [{"apple":25},{"bannana":36},{"orange":6}];
for(var i=0;i<fruits1.length;i++) {
for(var j=step;j<fruits2.length;j++) {
console.log("FRUIT1");
console.log(JSON.stringify(fruits1[i]));
console.log("FRUIT2");
console.log(JSON.stringify(fruits2[j]));
console.log("----------------------");
if(JSON.stringify(fruits1[i])!== JSON.stringify(fruits2[j])) {
//do something
}
step = step + 1;
break;
}
}
}
With an invention of Object.prototype.compare() and Array.prototype.compare() this job becomes a very simple task. Array compare can handle both primitive and reference type items. Objects are compared shallow. Let's see how it works;
Object.prototype.compare = function(o){
var ok = Object.keys(this);
return typeof o === "object" && ok.length === Object.keys(o).length ? ok.every(k => this[k] === o[k]) : false;
};
Array.prototype.compare = function(a){
return this.every((e,i) => typeof a[i] === "object" ? a[i].compare(e) : a[i] === e);
};
var fruits1 = [{"apple":25},{"bannana":36},{"orange":6}],
fruits2 = [{"apple":25},{"bannana":36},{"orange":6}];
console.log(fruits1.compare(fruits2));
Simple function without library:
var arr1 = [1,2,3];
var arr2 = [1,2,4];
//This function takes one item, the index of the item, and another array to compare the item with.
function compare(item, index, array2){
return array2[index] == item;
}
// the forEach method gives the item as first parameter
// the index as second parameter
// and the array as third parameter. All are optional.
arr1.forEach(function(item, index){
console.log(compare(item, index, arr2));
});
Combine this with the answer Abdennour TOUMI gave, and you have an object comparison method :)
For simple objects you could use JSON.stringify(obj1) === JSON.stringify(obj2).
More info on object comparison can be found in this answer
Use underscore array functions. I would go with intersection
http://underscorejs.org/#intersection
You can use the following static method for Object class : Object.equals
Object.equals=function(a,b){if(a===b)return!0;if(!(a instanceof Object&&b instanceof Object))return!1;if(a.valueOf()===b.valueOf())return!0;if(a.constructor!==b.constructor)return!1;for(var c in a)if(a.hasOwnProperty(c)){if(!b.hasOwnProperty(c))return!1;if(a[c]!==b[c]){if("object"!=typeof a[c])return!1;if(!Object.equals(a[c],b[c]))return!1}}for(c in b)if(b.hasOwnProperty(c)&&!a.hasOwnProperty(c))return!1;return!0};
console.log(
`"[1,2,3] == [1,2,3]" ?`,Object.equals([1,2,3],[1,2,3])
);
console.log(
`"[{"apple":25},{"bannana":36},{"orange":6}] == [{"apple":25},{"bannana":36},{"orange":6}]" ?`,Object.equals([{"apple":25},{"bannana":36},{"orange":6}], [{"apple":25},{"bannana":36},{"orange":6}])
);

JavaScript: Partial Function, why is necessary to do with Array.prototype.slice.call(arguments)

I wanted to do a partial_left function that will be executed into an array, something liek that:
array_1.map(partial_left1(equal));
function equal(x){return x;}
, but I crushed when I pushed the variables of arguments into an other array(and not with the Array.prototype.slice.call(arguments)). Because I wanna do other way, But I surprised when the result were differents:
var array_1=[1,2,3];
with my method: th result is : [1,1,1]//doing reference only to the array[0]
with Array,prototype.slice.call: [1,2,3]//how I would want to be
this is my code, where I push the values of arguments in another array:
function concat(arg1,arg2,n){
for (var i = n; i < arg2.length; i++)
arg1.push(arg2[i]);
return arg1;
}
function partial_left1(f){
var argum_apply=[];
argum_apply=concat(argum_apply,arguments,1);
return function(){
argum_apply=concat(argum_apply,arguments,0);
return f.apply(this,argum_apply);
};
}
And this is the code with Array.prototype.slice.call:
function array(a, n) { return Array.prototype.slice.call(a, n || 0); }
function partial_left2(f /*, ...*/) {
var args = arguments;
return function() {
var a = array(args, 1);
a = a.concat(array(arguments));
return f.apply(this, a);
};
}
A simple definition of equal with partial:
var equal_left1=partial_left1(equal);
var equal_left2=partial_left2(equal);
Here are the results, and I don't know why they are differents?
var array_1=[1,2,3];
alert(array_1.map(equal_left1));//1,1,1
alert(array_1.map(equal_left2));//1,2,3
Someones who knows please explain me what are the differences between "Concat" and use "Array.prototype.slice.call"?
One of the bad design decisions: arguments is not an array. It is array-like, in the sense it has a length property.
You can't call the concat method of arguments because it doesn't exist.
Array.prototype.slice.call uses the slice method from the arrays and invokes it -- it is a quick and easy way to turn something array-like to a proper array.
You can do the same thing with a loop, if you want:
var arr = [];
for (var i = 0; i < arguments.length; i++) {
arr[i] = arguments[i];
}

Get an object of a dictionary by attribute

I have a dictionary:
[ object , object, object, object, object ]
object contains: id and name.
I have an Id ('123456') and I want to get the object with this id.
Is there another solution how can I do it without for loop on the objects?
any help appreciated!
Hate loops, then go for recursion, i just assumed that you are having that array in a variable called as xArr
var xObj = check(0,"123456");
function check(cnt,id) {
if(xArr[cnt].id === id)
{
return xArr[cnt];
}
else if(cnt === xArr.length - 1) {
return null;
}
else {
cnt += 1;
return check(cnt, id);
}
}
That's an array, you could use jQuery.grep to get the elements with id "123456".
var result = $.grep(arr, function(obj) {
return obj.id === '123456';
});
Array also provide an .filter method (need a polyfill for browsers not support it):
var result = arr.filter(function(obj) {
return obj.id === '123456';
});
If you want to use a vanilla JS method, you can use filter. This pretty much does the same as $.grep.
var result = arr.filter(function (obj) {
return obj.id === '123456';
});

Check if value exists in JavaScript object

How would I check in my array of objects, if a specific item exists (in my case MachineId with id 2)?
[{"MachineID":"1","SiteID":"20"},{"MachineID":"2","SiteID":"20"},{"MachineID":"3","SiteID":"20"},{"MachineID":"4","SiteID":"20"}]
I tried this:
if (index instanceof machineIds.MachineID) {
alert('value is Array!');
} else {
alert('Not an array');
}
In cross browser way you may use jQuery.grep() method for it:
var item = $.grep(machineIds, function(item) {
return item.MachineID == index;
});
if (item.length) {
alert("value is Array!");
}
The simplest to understand solution is to loop over the array, and check each one.
var match;
for (var i = 0; i < yourArray.length; i++) {
if (yourArray[i].MachineId == 2)
match = yourArray[i];
}
Note if there is more than one matching item, this will return the last one. You can also dress this up in a function.
function findByMachineId(ary, value) {
var match;
for (var i = 0; i < ary.length; i++) {
if (ary[i].MachineId == value)
match = ary[i];
}
return match;
}
There are many standard solution, you don't need third party libraries or loop iteratively.
Array some method - since JavaScript 1.6.
Array find method - since ES6
Array findIndex method - since ES6
For example, using some();
var yourArray = [{"MachineID":"1","SiteID":"20"},{"MachineID":"2","SiteID":"20"},{"MachineID":"3","SiteID":"20"},{"MachineID":"4","SiteID":"20"}];
var params = {searchedID: "2", elementFound: null};
var isCorrectMachineID = function(element) {
if (element.MachineID == this.searchedID);
return (this.elementFound = element);
return false;
};
var isFound = yourArray.some(isCorrectMachineID, params)
Array some method accepts two parameters:
callback - Function to test for each element.
thisObject - Object to use as this when executing callback.
Callback function is not coupled with the iteration code and, using thisObject parameter, you can even return to the caller the element found or more data.
If such an element is found, some immediately returns true
http://jsfiddle.net/gu8Wq/1/
You could use this condition:
if (arr.filter(function(v){return this.MachineID == 2;}).length > 0)
Old question at this point, but here's an ES6 solution that uses Array.find:
let machine2 = machines.find((machine) => machine.id === '2');
if (machine2) {
// ...
}
var item = [{"MachineID":"1","SiteID":"20"},{"MachineID":"2","SiteID":"20"},{"MachineID":"3","SiteID":"20"},{"MachineID":"4","SiteID":"20"}];
var newItem = item.filter(function(i) {
return i.MachineID == 2; //it will return an object where MachineID matches with 2
});
console.log(newItem); // will print [{"MachineID":"2","SiteID":"20"}]

jQuery: Index of element in array where predicate

I have an array of objects. Each object has, among others, an ID attribute. I want to find the index in the array of the object with a specific ID. Is there any elegant and simple way to do this in jQuery?
See [`Array.filter`][1] to filter an array with a callback function. Each object in the array will be passed to the callback function one by one. The callback function must return `true` if the value is to be included, or false if not.
var matchingIDs = objects.filter(function(o) {
return o.ID == searchTerm;
});
All objects having the ID as searchTerm will be returned as an array to matchingIDs. Get the matching element from the first index (assuming ID is unique and there's only gonna be one)
matchingIDs[0];
[1]: https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter
Update:
Checkout findIndex from ECMAScript 6.
items.findIndex(function(item) { item.property == valueToSearch; });
Since findIndex isn't available on most browsers yet, you could backfill it using this implementation:
if (!Array.prototype.findIndex) {
Array.prototype.findIndex = function(predicate) {
if (this == null) {
throw new TypeError('Array.prototype.findIndex called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return i;
}
}
return -1;
};
}
In the case you should use for loop in javascript instead of using jQuery. See way 3 in http://net.tutsplus.com/tutorials/javascript-ajax/10-ways-to-instantly-increase-your-jquery-performance/
UPDATED: jQuery is written in javascript and it can not be faster than another code written also in javascript. jQuery is very good if you work with the DOM, but doesn't really help if you're working with simple javascript arrays or objects.
The code you're looking for can be something like this:
for (var i=0, l = ar.length; i<l; i++) {
if (ar[i].ID === specificID) {
// i is the index. You can use it here directly or make a break
// and use i after the loop (variables in javascript declared
// in a block can be used anywhere in the same function)
break;
}
}
if (i<l) {
// i is the index
}
Important that you should hold some simple javascript rules: Always declare local variables (don't forget var before variable declaration) and cache any properties or indexes that you use more than one time in a local variable (like ar.length above). (See for example http://wiki.forum.nokia.com/index.php/JavaScript_Performance_Best_Practices)
Not really elegant, but a cute trick:
var index = parseInt(
$.map(array, function(i, o) { return o.id === target ? i : ''; }).join('')
);
jQuery doesn't have a lot of functional constructs like that; the philosophy of the library is really focused on the job of DOM wrangling. They won't even add a .reduce() function because nobody can think of a reason it'd be useful to the core functionality.
The Underscore.js library has a lot of such facilities, and it "plays nice" with jQuery.
There are no built-in methods for this; the [].indexOf() method doesn't take a predicate, so you need something custom:
function indexOf(array, predicate)
{
for (var i = 0, n = array.length; i != n; ++i) {
if (predicate(array[i])) {
return i;
}
}
return -1;
}
var index = indexOf(arr, function(item) {
return item.ID == 'foo';
});
The function returns -1 if the predicate never yields a truthy value.
Update
There's Array.findIndex() that you could use now:
const arr = [{ID: 'bar'}, {ID: 'baz'}, {ID: 'foo'}];
const index = arr.findIndex(item => item.ID === 'foo');
console.log(index); // 2
Use jOrder. http://github.com/danstocker/jorder
Feed your array into a jOrder table, and add an index on the 'ID' field.
var table = jOrder(data)
.index('id', ['ID']);
Then, get the array index of an element by:
var arrayidx = table.index('id').lookup([{ ID: MyID }]);
If you want the entire row, then:
var filtered = table.where([{ ID: MyID }]);
Voila.

Categories