I want extend javascript Array to implement map2 functionality without using inbuilt map function. Where map2 functionality doubles the passed value of the array.
For example:
var m = [1,2,3,4,5]
var double = [1,2,3,4,5].map2(doubleFn)
console.log(double) should output 2,4,6,8,10
And above functionality need be developed by without using any in built method of JS array
Code Snippet
Array.prototype.map2= function(callback, thisArg){
var len=this.length
for(var i in this){
callback.call(this,this[i]*2)
}
}
Please let me know, what approach can I follow to do this
Array.prototype.map2 = function (callback, thisArg){
var i, el,
len = this.length,
res = [],
_this = thisArg ? thisArg : this;
for (i = 0; i < len; i++) {
el = this[i]; // also you can use this[i] * 2 - it depend what do want;
res[i] = callback.call(_this, el);
}
return res;
};
var double = [1,2,3,4,5].map2(function (el) {
return el * 2;
});
Example
About you errors, don't use for..in for Arrays., in map you need create new array and return it...
I'm guessing that you want to re implement built in Array.prototype.map.
Here's one of the way of doing it:
Array.prototype.map2 = function(f1){
var a = [];
this.forEach(function(element){
a.push(f1(element));
})
return a;
}
a = [1,2,3,4,5]
console.log(a.map2(function(a){return a<<1;});
//output: [ 2, 4, 6, 8, 10 ]
Edit: without using inbuilt functions:
Array.prototype.map2 = function(f1){
var a = [];
var that = this;
return (function recArray(index, target){
if(typeof(that[index]) !== 'undefined') {
target[index] = f1(that[index]);
return recArray(index + 1, target);
}
return target;
})(0, a);
}
But there is a problem with this solution:
What if input Array has holes:
a = [1,2,3,4,5]
a[12] = 11
//now a is: [ 1, 2, 3, 4, 5, , , , , , , , 11 ]
JavaScript Arrays can have holes and if there are undefined values in between then above method will fail. Without knowing the length of the Array, it is impossible to traverse it if it contains 'holes'.
Explicitly storing length doesn't makes sense as it is stored in Array.prototype.length.
Hence if the array is not contiguous then without using length it is impossible to implement map.
Related
So I am trying to create a method that mimic exactly what the Array.prototype.map() method does and there is a lot I am confused about.
The main problem I suppose comes down to its syntax. I know there are many different ways to utilitize the map method for instance:
example 1:
let say I have an array of objects -
var movieList = [
{"Title" : "Inception",
"Awards" : "4 oscars",
"Language" : "English",
"imdbRating" : "8.8"
},
{"Title" : "Inception2",
"Awards" : "44 oscars",
"Language" : "English and Spanish",
"imdbRating" : "9.8"
},
{"Title" : "Interstellar",
"Awards" : "10 oscars",
"Language" : "English",
"imdbRating" : "9.5"
}
];
Lets say I want to make a function that returns a list of objects that contains only the title of the movie and its imdbRating. In this case, I can use the map() method:
let newList = movieList.map( (current) ({'title': current['Title'],
'rating': current['imdbRating'] }) );
the above line of code satisfies what i need to achieve my objective using the map method. However, the syntax can be different for other cases
example 2:
let s = [1, 2, 3, 4];
let s2 = s.map( function(item) {
return item*2;
});
using the map function s2 will return an array that has for each element double the value of each element in the s array.
Now, going back to the theme of this post, the problem I am working on gave me this outline to start with:
Array.prototype.myMap = function(callback) {
let newArray = [];
I understand that when an array calls on the myMap method, it is inserting 2 arguments, the array and a function. But I cannot wrap my head around how I can concretely assign the value of each callback function on the element to the newArray in the myMap method. Especially because I do not know how to access the original array.
One of my attempts that I know is ridiculous because I do not know how I can access the length of the array as well as the array itself which calls on the myMap method-
Array.prototype.myMap = function(callback) {
let newArray = [];
let x = this.length();
for(let i=0; i<x; i++){
let counter = callback();
newArray.push(counter);
}
return newArray;
};
the way I've understood the map() method thus far, is it takes 3 arguments, an array, a function, and the element that will be put thru the function and I do not know the syntax well enough to iterate over the array that calls on the map method and nowhere in my course have I been taught how to do this and I have not found any online resource either that offers a solution to this problem.
length is not a method - it's just a property. And you need to pass this[i] to the callback for the correct output.
Array.prototype.myMap = function(callback) {
let newArray = [];
let x = this.length;
for (let i = 0; i < x; i++) {
let counter = callback(this[i]);
newArray.push(counter);
}
return newArray;
};
let arr = [1, 2, 3];
arr = arr.myMap(e => e * 2);
console.log(arr);
Please note it's quite bad practice to mutate the prototype methods - and especially creating a new one when an identical method exists. (map has all the functionality of myMap plus more).
This is simplified version of the actual map Polyfill. You need to get the length using this.length. And pass the current item in loop, it's index, and the array itself as a parameter to callback
Array.prototype.myMap = function(callback, thisArg) {
const newArray = [];
const length = this.length;
for (let i = 0; i < length; i++) {
let value = callback(this[i], i, this); // call with proper arguments
newArray.push(value);
}
return newArray;
};
const arr = [1, 2, 3];
console.log(arr.myMap(a => a * 2))
Note: map method also takes a thisArg as parameter. If you want to use that, you need to call the callback with thisArg as this
callback.call(thisArg, this[i], i, this);
Like this answer, but correctly preserves empty items.
Array.prototype.map = function (callback, thisArg) {
let arr = [];
const len = this.length;
for (let i = 0; i < len; i++) {
if (i in this) arr.push(callback.call(thisArg, this[i], i, this));
else arr.length++;
}
return arr;
};
Here is where you can learn about javascript map function more:
https://www.w3schools.com/jsref/jsref_map.asp
Implementing map method is actually quite easier than you think. Here is a really simple example that would do exactly what the map method do from https://www.w3schools.com/jsref/jsref_map.asp:
Array.prototype.myMap = function(callback) {
arr = [];
for (var i = 0; i < this.length; i++)
arr.push(callback(this[i], i, this));
return arr;
};
//tests
var numbers2 = [1, 4, 9];
var squareRoot = numbers2.myMap(function(num) {
return Math.sqrt(num);
});
console.log(squareRoot); // [ 1, 2, 3 ]
So, the map method simply returns an array by whose values map to the original array's values by running a specified function given as an argument on each of the values.
Note: The method skips the optional parameter of the original map function which specifies what will be the this value
Here is a working version of your myMap function.
A part of imitating these methods that may confuse people is how the this keyword works in javascript. Here is an MDN Reference article.
Array.prototype.myMap = function(callback, thisArg) {
let newArray = [];
let x = this.length;
for(let i=0; i<x; i++){
let counter = callback.call(thisArg, this[i], i, this);
newArray.push(counter);
}
return newArray;
};
Array.prototype.mappy = function (callback, thisArg) {
let newArray = [];
for (let i = 0; i < this.length; i++) {
if (i in this) newArray.push(callback.call(thisArg, this[i], i, this));
else newArray.push(undefined);
}
return newArray;
};
You could take advantage of the spread operator and do something like this:
Map.prototype.slice = function(start, end) {
return new Map([...this].slice(start, end));
};
And in use:
const testMap = new Map([["a", 1], ['b', 2], ["c", 3], ["d", 4], ["e", 5]]);
console.log(testMap); // Map(5) { 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5 }
console.log(testMap.slice(0, 2)); // Map(2) { 'a' => 1, 'b' => 2 }
You can definitely build upon this since this still has the caveats of Array.prototype.slice().
You can also use forEach if you don't want to use a for loop
Array.prototype.myMap = function (callback) {
let newArr = [];
this.forEach((item) => {
let elem = callback(item);
newArr.push(elem);
});
return newArr;
};
I want to double the key values in an array of objects and I always end up getting the last key:val in the list. I want to be able to print the whole array of objects doubled their values.
doubleArr = [];
function doubleSize (a,b) {
for(var i in a) {
if (a.hasOwnProperty(i)) {
var doubleObj = {};
doubleObj[i] = b * a[i];
var results = doubleObj[i];
doubleArr.push(results);
}
}
return doubleObj;
}
console.log(doubleSize({1:1,1:2,1:3,1:4,1:5}, 2))
I only get {1:10}. The goal is to get {1:2,1:4,1:6,1:8,1:10}. Thanks.
The primary issue with your code is that you cannot have duplicate keys in an object. Every key in an object must be unique; otherwise, you are essentially redeclaring the value associated with that key over and over (so, naturally, in your example, the key 1 will end up being associated with the last value you assign it: 5).
However, even if you used an object without duplicate keys, you still have the following issues:
Your doubleArr should not be declared in the global scope, but instead within the function; moreover, you don't need an array for what you're doing
Your code is actually constructing an array of objects rather than an object. If you want to return an object, you should build up an object with unique values in your function.
Here's an example of a modified version of your code:
function doubleSize (a,b) {
var doubleObj = {};
for(var i in a) {
if (a.hasOwnProperty(i)) {
doubleObj[i] = b * a[i];
}
}
return doubleObj;
}
console.log(doubleSize({1:1,2:2,3:3,4:4,5:5}, 2)) // Note the unique keys
However, I don't fully understand the need for objects. You could use arrays instead, and then replace all of your code with a simple one-liner using Array.map:
var arr = [1, 2, 3, 4, 5]
var doubledArr = arr.map(x => 2*x) // An arrow function that multiplies each value in the array by 2
console.log(doubledArr)
You are using the same key for your objects 1. I am assuming that you don't need objects as they don't provide anything obvious to your code:
Edit If you insist on using objects, I have provided the second version also.
function doubleSize (a,b)
{
var doubleArr = [];
for(var i=0; i<a.length;i++)
{
doubleArr.push(a[i]*b);
}
return doubleArr;
}
console.log(doubleSize([1,2,3,4,5], 2))
function doubleSize2 (a,b)
{
var result = {};
for(var i in a)
{
result[i] = a[i]*b
}
return result;
}
console.log(doubleSize2({a:1,b:2,c:3,d:4,e:5}, 2))
You can't have duplicate keys in an object, you can have an array of objects with the same key though. Something like:
function doubleSize (a, b) {
var out = [];
for(var i = 0; i < a.length; i++) {
out.push({'1': a[i]['1'] * b});
}
return out;
}
console.log(doubleSize([{'1': 1}, {'1': 2}, {'1': 3}, {'1': 4}, {'1': 5}], 2));
function doubleSize (a,b) {
var midObject = {};
for(var i in a) {
if (a.hasOwnProperty(i)) {
midObject[i] = b * a[i];
}
}
return midObject;
}
console.log(doubleSize({1:1,2:2,3:3,4:4,5:5}, 5))
I need to know if one or more duplicates exist in a list. Is there a way to do this without travelling through the list more than once?
Thanks guys for the suggestions. I ended up using this because it was the simplest to implement:
var names = [];
var namesLen = names.length;
for (i=0; i<namesLen; i++) {
for (x=0; x<namesLen; x++) {
if (names[i] === names[x] && (i !== x)) {alert('dupe')}
}
}
Well the usual way to do that would be to put each item in a hashmap dictionary and you could check if it was already inserted. If your list is of objects they you would have to create your own hash function on the object as you would know what makes each one unique. Check out the answer to this question.
JavaScript Hashmap Equivalent
This method uses an object as a lookup table to keep track of how many and which dups were found. It then returns an object with each dup and the dup count.
function findDups(list) {
var uniques = {}, val;
var dups = {};
for (var i = 0, len = list.length; i < len; i++) {
val = list[i];
if (val in uniques) {
uniques[val]++;
dups[val] = uniques[val];
} else {
uniques[val] = 1;
}
}
return(dups);
}
var data = [1,2,3,4,5,2,3,2,6,8,9,9];
findDups(data); // returns {2: 3, 3: 2, 9: 2}
var data2 = [1,2,3,4,5,6,7,8,9];
findDups(data2); // returns {}
var data3 = [1,1,1,1,1,2,3,4];
findDups(data3); // returns {1: 5}
Since we now have ES6 available with the built-in Map object, here's a version of findDups() that uses the Map object:
function findDups(list) {
const uniques = new Set(); // set of items found
const dups = new Map(); // count of items that have dups
for (let val of list) {
if (uniques.has(val)) {
let cnt = dups.get(val) || 1;
dups.set(val, ++cnt);
} else {
uniques.add(val);
}
}
return dups;
}
var data = [1,2,3,4,5,2,3,2,6,8,9,9];
log(findDups(data)); // returns {2 => 3, 3 => 2, 9 => 2}
var data2 = [1,2,3,4,5,6,7,8,9];
log(findDups(data2)); // returns empty map
var data3 = [1,1,1,1,1,2,3,4];
log(findDups(data3)); // returns {1 => 5}
// display resulting Map object (only used for debugging display in snippet)
function log(map) {
let output = [];
for (let [key, value] of map) {
output.push(key + " => " + value);
}
let div = document.createElement("div");
div.innerHTML = "{" + output.join(", ") + "}";
document.body.appendChild(div);
}
If your strings are in an array (A) you can use A.some-
it will return true and quit as soon as it finds a duplicate,
or return false if it has checked them all without any duplicates.
has_duplicates= A.some(function(itm){
return A.indexOf(itm)===A.lastIndexOf(itm);
});
If your list was just words or phrases, you could put them into an associative array.
var list=new Array("foo", "bar", "foobar", "foo", "bar");
var newlist= new Array();
for(i in list){
if(newlist[list[i]])
newlist[list[i]]++;
else
newlist[list[i]]=1;
}
Your final array should look like this:
"foo"=>2, "bar"=>2, "foobar"=>1
I have a data dictionary like this:
var data = {
'text1': 1,
'text2': 2,
'text3': 3,
...
'text20': 20
];
I need to pick a random selection of those keys and then shuffle it's values. In the example, it should write something like this:
> console.log(choose(data, 5));
[ { key: 'text15', value: 8 },
{ key: 'text6', value: 3 },
{ key: 'text3', value: 15 },
{ key: 'text19', value: 6 },
{ key: 'text8', value: 19 } ]
For now I'm extracting the keys into another array and sorting by Math.random() but I'm stuck at swaping the values because no key should have the same value it initially had.
How would you swap key/values here?
Thanks
I put together a possible solution using underscore.js to simplify traversing the object and arrays in a cross browser manner:
var data = {
text1: 1,
text2: 2,
text3: 3,
text4: 4,
text5: 5,
text6: 6,
text7: 7,
text8: 8,
text9: 9,
text10: 10
};
function choose(data, num)
{
var keys = _.sortBy(
_.keys(data),
function(k)
{
return (Math.random() * 3) - 1;
}
),
results = [],
k1, k2;
if (num > keys.length) {
throw new Error('Impossible to retrieve more values than exist');
}
while (results.length < num) {
k1 = k2 || keys.pop();
k2 = keys.pop();
results.push({key:k1, value: data[k2]});
}
return results;
}
console.log(choose(data, 5));
This isn't necessarily an optimal approach but it seems to meet your requirements. I first grab all of the keys and sort them randomly. I then loop through the random keys creating a new object with one key and the following keys value. That way you'll always end up with a different value associated with each key. If you need it to work when the value of num passed in to the function == the number of keys in the data then you'll have to add a little more code - I'll leave that as an exercise for the reader :)
You can have a play with this code on jsfiddle:
http://jsfiddle.net/zVyQW/1/
You could do this:
collect names and corresponding values in two arrays names and values
shuffle both arrays independently of each other
take the first n items of both arrays and combine them
Here’s an example implementation:
Array.prototype.shuffle = function() {
for (var i=this.length-1, j, tmp; i>0; i--) {
j = Math.round(Math.random()*i);
tmp = this[i], this[i] = this[j], this[j] = tmp;
}
return this;
};
function choose(data, number) {
var names = [], values = [], pick = [];
for (var name in data) {
if (data.hasOwnProperty(name)) {
names.push(name);
values.push(data[name]);
}
}
names = names.shuffle(), values = values.shuffle();
for (var i=Math.min(number >>> 0, names.length-1); i>=0; i--) {
pick.push({key: names[i], value: values[i]});
}
return pick;
}
Been a while since this was answered, but I was working on shuffling and found the following to be by far the fastest implementation with an evenly random distribution.
It's fast because it only makes one call to Math.random on each iteration, all the rest is done by property access. It doesn't modify the array, just reassigns values.
function shuffle(a) {
var t, j, i=a.length, rand=Math.random;
// For each element in the array, swap it with a random
// element (which might be itself)
while (i--) {
k = rand()*(i+1)|0;
t = a[k];
a[k]=a[i];
a[i]=t;
}
return a;
}
It uses a combination of three functions (including the Array shuffle prototype method).
Here is the complete code:
var obj = {
"red":"RED",
"blue":"BLUE",
"green":"GREEN",
"yellow":"YELLOW",
"purple":"PURPLE"
};
Array.prototype.shuffle = function(){
for (var i = 0; i < this.length; i++){
var a = this[i];
var b = Math.floor(Math.random() * this.length);
this[i] = this[b];
this[b] = a;
}
}
obj = shuffleProperties(obj); // run shuffle
function shuffleProperties(obj) {
var new_obj = {};
var keys = getKeys(obj);
keys.shuffle();
for (var key in keys){
if (key == "shuffle") continue; // skip our prototype method
new_obj[keys[key]] = obj[keys[key]];
}
return new_obj;
}
function getKeys(obj){
var arr = new Array();
for (var key in obj)
arr.push(key);
return arr;
}
for(key in obj){
alert(key);
}
Check all post,
Best Regards.
Use an implementation of random that randomizes a discrete set of values, such as Math.rand seen here. For each index, randomize Math.rand(index, length-1) to get a list of random indexes, the location off all indices will change.
status_name=Array("a","b","c","b","e","f");
status_id=Array( 1, 2, 3, 4, 5, 6);
How to combine these two arrays and to built multi dimensional array Expected Multidimensional array be like this
[["a", 1],["b", 2],["c", 3],["d", 4],["e", 5],["f", 6]]
Help me how to use above two array values and built my expected multidimensional array
Since you're including jQuery, you can use jQuery.map in a similar fashion to Linus' answer:
var result = [],
status_name = ["a","b","c","b","e","f"],
status_id = [1, 2, 3, 4, 5, 6];
result = $.map(status_name, function (el, idx) {
return [[el, status_id[idx]]];
});
Looking at your variable names, I'd guess that your coming from a language (like PHP). If that's the case, make sure you remember to declare local variables with the var keyword, otherwise you'll be polluting the global scope and you'll run into some hideous bugs in IE.
JavaScript has no buitin method for this, but you can easily write it yourself:
function zip(arrayA, arrayB) {
var length = Math.min(arrayA.length, arrayB.length);
var result = [];
for (var n = 0; n < length; n++) {
result.push([arrayA[n], arrayB[n]]);
}
return result;
}
The name zip is chosen because a function that does something like this is often called zip in other languages.
I tried Myself and brought this solution, It might help some one
status_name=Array("a","b","c","b","e","f");
status_id=Array( 1, 2, 3, 4, 5, 6);
Script:
Values=[];
for (i = 0; i < status_name.length; ++i)
{
Values[i] =Array(status_name[i], status_id[i]);
}
Using jQuery.map
var status_name = ["a","b","c","b","e","f"],
status_id = [1,2,3,4,5,6],
r = [];
r = $.map(status_name, function(n, i) {
return [[n, status_id[i]]];
});
Note the difference between return [[n, status_id[i]]] and return [n, status_id[i]]. Using the former will result in a 2d array while using the latter will result in a 1d array.
var combined = [], length = Math.min(status_name.length, status_id.length);
for(var i = 0; i < length; i++) {
combined.push([status_name[i], status_id[i]]);
}
You could also use Array.prototype.map, but that's not supported in all browsers:
var combined = status_name.map(function(name, index) { return [name, status_id[index]] });
try
function array_combine (keys, values) {
// Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values
//
// version: 1102.614
// discuss at: http://phpjs.org/functions/array_combine
// + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: Brett Zamir (http://brett-zamir.me)
// * example 1: array_combine([0,1,2], ['kevin','van','zonneveld']);
// * returns 1: {0: 'kevin', 1: 'van', 2: 'zonneveld'}
var new_array = {},
keycount = keys && keys.length,
i = 0;
// input sanitation
if (typeof keys !== 'object' || typeof values !== 'object' || // Only accept arrays or array-like objects
typeof keycount !== 'number' || typeof values.length !== 'number' || !keycount) { // Require arrays to have a count
return false;
}
// number of elements does not match
if (keycount != values.length) {
return false;
}
for (i = 0; i < keycount; i++) {
new_array[keys[i]] = values[i];
}
return new_array;
Reference
- arr combine
- array combine