can I convert , in Javascript, an object into an array in Internet Explorer? I read that the method Array.from(obj) is not supported from IE. It is correct?
Thank you
You can verify for yourself on On Mozilla's MDN that Array.from() isn't supported by IE :
On that same page, you can also find the following polyfill to add support of Array.from() to browsers that don't support it natively :
// Production steps of ECMA-262, Edition 6, 22.1.2.1
if (!Array.from) {
Array.from = (function () {
var toStr = Object.prototype.toString;
var isCallable = function (fn) {
return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
};
var toInteger = function (value) {
var number = Number(value);
if (isNaN(number)) { return 0; }
if (number === 0 || !isFinite(number)) { return number; }
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
};
var maxSafeInteger = Math.pow(2, 53) - 1;
var toLength = function (value) {
var len = toInteger(value);
return Math.min(Math.max(len, 0), maxSafeInteger);
};
// The length property of the from method is 1.
return function from(arrayLike/*, mapFn, thisArg */) {
// 1. Let C be the this value.
var C = this;
// 2. Let items be ToObject(arrayLike).
var items = Object(arrayLike);
// 3. ReturnIfAbrupt(items).
if (arrayLike == null) {
throw new TypeError('Array.from requires an array-like object - not null or undefined');
}
// 4. If mapfn is undefined, then let mapping be false.
var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
var T;
if (typeof mapFn !== 'undefined') {
// 5. else
// 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
if (!isCallable(mapFn)) {
throw new TypeError('Array.from: when provided, the second argument must be a function');
}
// 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 2) {
T = arguments[2];
}
}
// 10. Let lenValue be Get(items, "length").
// 11. Let len be ToLength(lenValue).
var len = toLength(items.length);
// 13. If IsConstructor(C) is true, then
// 13. a. Let A be the result of calling the [[Construct]] internal method
// of C with an argument list containing the single item len.
// 14. a. Else, Let A be ArrayCreate(len).
var A = isCallable(C) ? Object(new C(len)) : new Array(len);
// 16. Let k be 0.
var k = 0;
// 17. Repeat, while k < len… (also steps a - h)
var kValue;
while (k < len) {
kValue = items[k];
if (mapFn) {
A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
} else {
A[k] = kValue;
}
k += 1;
}
// 18. Let putStatus be Put(A, "length", len, true).
A.length = len;
// 20. Return A.
return A;
};
}());
}
Note that it comes with the following remarks :
This algorithm is
exactly the one specified in ECMA-262, 6th edition, assuming Object
and TypeError have their original values and that callback.call
evaluates to the original value of Function.prototype.call. In
addition, since true iterables can not be polyfilled, this
implementation does not support generic iterables as defined in the
6th edition of ECMA-262.
That means that there are a few caveats, but it should be more than sufficient for most purposes!
Yes, according to ECMAScript 6, this true for elder IE. You can find some polyfills listed there if you need this function & support for elder IE.
Array.from method is a method that came with ECMAScript 6
you can see on this page that the support for ECMAScript 6 in IE is not very good. Look at the examples in the below question for a solution to your problem
Converting a JS object to an array
As in JavaScript, array positions are like object properties (You can access objects with obj["property"]), you can just iterate over the properties of your object and push them onto an array.
For example:
//Example object
var obj = {
prop1: "asd",
prop2: "Another value",
prop3: 5,
funcprop: function(val){return val*2;}
}
//-----------
var i = 0;
var array = [];
for(prop in obj){
array[i] = obj[prop];
i++;
}
console.log(array); //Array [ "asd", "Another value", 5, funcprop() ]
Acording to MSDN, for..in syntax is valid since IE6.
Take this with care, as you will have property functions in the array positions also.
Related
Studying the polyfill for the find method written on the MDN web docs, there's a particular line that I'm not following, let me share the code
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
value: function(predicate) {
if (this == null) {
throw TypeError('"this" is null or not defined');
}
var o = Object(this);
var len = o.length >>> 0;
if (typeof predicate !== 'function') {
throw TypeError('predicate must be a function');
}
var thisArg = arguments[1];
var k = 0;
while (k < len) {
var kValue = o[k];
if (predicate.call(thisArg, kValue, k, o)) {
return kValue;
}
k++;
}
return undefined;
},
configurable: true,
writable: true
});
}
My question is with the expression var o = Object(this);. What's the purpose of doing so instead of doing var o = this?. Printing the value in both described cases returns the same object.
Is this an abbreviated way of calling var o = new Object(this);?
I have removed the comments from the method to shorten the text, here's the link to the polyfill implementation.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find#Polyfill
Thanks!
In strict mode, this isn't always an object. Object(this) makes sure that o is an object, not a primitive.
Here's an example of this being a primitive:
"use strict";
Object.defineProperty(String.prototype, "nishCap", {
writable: true,
configurable: true,
value() {
console.log(typeof this); // "string"
const o = Object(this);
console.log(typeof o); // "object"
return o.substring(0,1).toUpperCase() + o.substring(1);
}
});
const capped = "foo".nishCap();
Note that this even applies to array methods, because you can call them on non-arrays, like Array.prototype.find.call("foo", ch => ch === "o").
Is this an abbreviated way of calling var o = new Object(this);?
No, new Object always creates a new object (and doesn't use the argument you give it). When you call Object as a function, it coerces its argument to object. So a primitive string becomes a String object, a primitive number becomes a Number object, etc.
What's the purpose of doing so instead of doing var o = this?
That polyfill is fairly closely following the spec, which starts out with:
Let O be ? ToObject(this value).
In most cases it wouldn't be important, but I wouldn't be surprised if there were some edge case where leaving it out would cause observable behavior at variance with the spec.
Because Array.prototype.find can be called with a this value which is not an object. See the specification:
When the find method is called, the following steps are taken:
Let O be ? ToObject(this value).
So, in order to be perfectly compliant with the specification, the polyfill needs Object(this). Otherwise, the implementations will not be the same, as you can see by the following two snippets:
'use strict';
const str = 'abc';
Array.prototype.find.call(
str,
(char, i, theThis) => {
// theThis should be an object, even if called on a non-object:
console.log(theThis);
}
);
'use strict';
Object.defineProperty(Array.prototype, 'find', {
value: function(predicate) {
if (this == null) {
throw TypeError('"this" is null or not defined');
}
// Without object wrapping:
var o = this; // <-------------------------------
var len = o.length >>> 0;
if (typeof predicate !== 'function') {
throw TypeError('predicate must be a function');
}
var thisArg = arguments[1];
var k = 0;
while (k < len) {
var kValue = o[k];
if (predicate.call(thisArg, kValue, k, o)) {
return kValue;
}
k++;
}
return undefined;
},
configurable: true,
writable: true
});
const str = 'abc';
Array.prototype.find.call(
str,
(char, i, theThis) => {
// The polyfill above had no object wrapping, so this result is not the same:
console.log(theThis);
}
);
I am using node, mocha, and chai for my application. I want to test that my returned results data property is the same "type of object" as one of my model objects (Very similar to chai's instance). I just want to confirm that the two objects have the same sets of property names. I am specifically not interested in the actual values of the properties.
Let's say I have the model Person like below. I want to check that my results.data has all the same properties as the expected model does. So in this case, Person which has a firstName and lastName.
So if results.data.lastName and results.data.firstName both exist, then it should return true. If either one doesn't exist, it should return false. A bonus would be if results.data has any additional properties like results.data.surname, then it would return false because surname doesn't exist in Person.
This model
function Person(data) {
var self = this;
self.firstName = "unknown";
self.lastName = "unknown";
if (typeof data != "undefined") {
self.firstName = data.firstName;
self.lastName = data.lastName;
}
}
You can serialize simple data to check for equality:
data1 = {firstName: 'John', lastName: 'Smith'};
data2 = {firstName: 'Jane', lastName: 'Smith'};
JSON.stringify(data1) === JSON.stringify(data2)
This will give you something like
'{firstName:"John",lastName:"Smith"}' === '{firstName:"Jane",lastName:"Smith"}'
As a function...
function compare(a, b) {
return JSON.stringify(a) === JSON.stringify(b);
}
compare(data1, data2);
EDIT
If you're using chai like you say, check out http://chaijs.com/api/bdd/#equal-section
EDIT 2
If you just want to check keys...
function compareKeys(a, b) {
var aKeys = Object.keys(a).sort();
var bKeys = Object.keys(b).sort();
return JSON.stringify(aKeys) === JSON.stringify(bKeys);
}
should do it.
2 Here a short ES6 variadic version:
function objectsHaveSameKeys(...objects) {
const allKeys = objects.reduce((keys, object) => keys.concat(Object.keys(object)), []);
const union = new Set(allKeys);
return objects.every(object => union.size === Object.keys(object).length);
}
A little performance test (MacBook Pro - 2,8 GHz Intel Core i7, Node 5.5.0):
var x = {};
var y = {};
for (var i = 0; i < 5000000; ++i) {
x[i] = i;
y[i] = i;
}
Results:
objectsHaveSameKeys(x, y) // took 4996 milliseconds
compareKeys(x, y) // took 14880 milliseconds
hasSameProps(x,y) // after 10 minutes I stopped execution
If you want to check if both objects have the same properties name, you can do this:
function hasSameProps( obj1, obj2 ) {
return Object.keys( obj1 ).every( function( prop ) {
return obj2.hasOwnProperty( prop );
});
}
var obj1 = { prop1: 'hello', prop2: 'world', prop3: [1,2,3,4,5] },
obj2 = { prop1: 'hello', prop2: 'world', prop3: [1,2,3,4,5] };
console.log(hasSameProps(obj1, obj2));
In this way you are sure to check only iterable and accessible properties of both the objects.
EDIT - 2013.04.26:
The previous function can be rewritten in the following way:
function hasSameProps( obj1, obj2 ) {
var obj1Props = Object.keys( obj1 ),
obj2Props = Object.keys( obj2 );
if ( obj1Props.length == obj2Props.length ) {
return obj1Props.every( function( prop ) {
return obj2Props.indexOf( prop ) >= 0;
});
}
return false;
}
In this way we check that both the objects have the same number of properties (otherwise the objects haven't the same properties, and we must return a logical false) then, if the number matches, we go to check if they have the same properties.
Bonus
A possible enhancement could be to introduce also a type checking to enforce the match on every property.
If you want deep validation like #speculees, here's an answer using deep-keys (disclosure: I'm sort of a maintainer of this small package)
// obj1 should have all of obj2's properties
var deepKeys = require('deep-keys');
var _ = require('underscore');
assert(0 === _.difference(deepKeys(obj2), deepKeys(obj1)).length);
// obj1 should have exactly obj2's properties
var deepKeys = require('deep-keys');
var _ = require('lodash');
assert(0 === _.xor(deepKeys(obj2), deepKeys(obj1)).length);
or with chai:
var expect = require('chai').expect;
var deepKeys = require('deep-keys');
// obj1 should have all of obj2's properties
expect(deepKeys(obj1)).to.include.members(deepKeys(obj2));
// obj1 should have exactly obj2's properties
expect(deepKeys(obj1)).to.have.members(deepKeys(obj2));
Here's a deep-check version of the function provided above by schirrmacher.
Below is my attempt. Please note:
Solution does not check for null and is not bullet proof
I haven't performance tested it. Maybe schirrmacher or OP can do that and share for the community.
I'm not a JS expert :).
function objectsHaveSameKeys(...objects) {
const allKeys = objects.reduce((keys, object) => keys.concat(Object.keys(object)), [])
const union = new Set(allKeys)
if (union.size === 0) return true
if (!objects.every((object) => union.size === Object.keys(object).length)) return false
for (let key of union.keys()) {
let res = objects.map((o) => (typeof o[key] === 'object' ? o[key] : {}))
if (!objectsHaveSameKeys(...res)) return false
}
return true
}
Update 1
A 90% improvement on the recursive deep-check version is achieved on my computer by skipping the concat() and adding the keys directly to the Set(). The same optimization to the original single level version by schirrmacher also achieves ~40% improvement.
The optimized deep-check is now very similar in performance to the optimized single level version!
function objectsHaveSameKeysOptimized(...objects) {
let union = new Set();
union = objects.reduce((keys, object) => keys.add(Object.keys(object)), union);
if (union.size === 0) return true
if (!objects.every((object) => union.size === Object.keys(object).length)) return false
for (let key of union.keys()) {
let res = objects.map((o) => (typeof o[key] === 'object' ? o[key] : {}))
if (!objectsHaveSameKeys(...res)) return false
}
return true
}
Performance Comparison
var x = {}
var y = {}
var a = {}
for (var j = 0; j < 10; ++j){
a[j] = j
}
for (var i = 0; i < 500000; ++i) {
x[i] = JSON.parse(JSON.stringify(a))
y[i] = JSON.parse(JSON.stringify(a))
}
let startTs = new Date()
let result = objectsHaveSameKeys(x, y)
let endTs = new Date()
console.log('objectsHaveSameKeys = ' + (endTs - startTs)/1000)
Results
A: Recursive/deep-check versions*
objectsHaveSameKeys = 5.185
objectsHaveSameKeysOptimized = 0.415
B: Original non-deep versions
objectsHaveSameKeysOriginalNonDeep = 0.517
objectsHaveSameKeysOriginalNonDeepOptimized = 0.342
function getObjectProperties(object, propertiesString = '') {
let auxPropertiesString = propertiesString;
for (const objectLevel of Object.keys(object).sort((a, b) => a.localeCompare(b))) {
if (typeof object[objectLevel] === 'object') {
auxPropertiesString += getObjectProperties(object[objectLevel], auxPropertiesString);
} else {
auxPropertiesString += objectLevel;
}
}
return auxPropertiesString;
}
function objectsHaveTheSameKeys(objects) {
const properties = [];
for (const object of objects) {
properties.push(getObjectProperties(object));
}
return properties.every(eachProperty => eachProperty === properties[0]);
}
It's a bit rudimentary, but should do the work in case you want to compare properties.
Legacy Browser Object Compare Function
Unlike the other solutions posted here, my Object Compare Function works in ALL BROWSERS, modern or legacy, including very old browsers, even Internet Explorer 5 (c.2000)!
Features:
Can compare an unlimited list of Objects. All must match or fails!
Ignores property order
Only compares "own" properties (i.e. non-prototype)
Matches BOTH property names and property values (key-value pairs)!
Matches functions signatures in objects!
Every object submitted is cross-compared with each other to detect missing properties in cases where one is missing but not in the other
Avoids null, undefined, NaN, Arrays, non-Objects, etc.
{} empty object detection
Works in almost ALL BROWSERS, including even Internet Explorer 5 and many other legacy browsers!
Note the function does not detect complex objects in properties, but you could rewrite the function to call them recursively.
Just call the method with as many objects as you like!
ObjectCompare(myObject1,myObject2,myObject3)
function ObjectCompare() {
try {
if (arguments && arguments.length > 0) {
var len = arguments.length;
if (len > 1) {
var array = [];
for (var i = 0; i < len; i++) {
if (
((typeof arguments[i] !== 'undefined') || (typeof arguments[i] === 'undefined' && arguments[i] !== undefined))
&& (arguments[i] !== null)
&& !(arguments[i] instanceof Array)
&& ((typeof arguments[i] === 'object') || (arguments[i] instanceof Object))
) {
array.push(arguments[i]);
}
}
if (array.length > 1) {
var a1 = array.slice();
var a2 = array.slice();
var len1 = a1.length;
var len2 = a2.length;
var noKeys = true;
var allKeysMatch = true;
for (var x = 0; x < len1; x++) {
console.log('---------- Start Object Check ---------');
//if (len2>0) {
// a2.shift();// remove next item
//}
len2 = a2.length;
if (len2 > 0 && allKeysMatch) {
for (var y = 0; y < len2; y++) {
if (x !== y) {// ignore objects checking themselves
//console.log('Object1: ' + JSON.stringify(a1[x]));
//console.log('Object2: ' + JSON.stringify(a2[y]));
console.log('Object1: ' + a1[x].toString());
console.log('Object2: ' + a2[y].toString());
var ownKeyCount1 = 0;
for (var key1 in a1[x]) {
if (a1[x].hasOwnProperty(key1)) {
// ---------- valid property to check ----------
ownKeyCount1++;
noKeys = false;
allKeysMatch = false;// prove all keys match!
var ownKeyCount2 = 0;
for (var key2 in a2[y]) {
if (a2[y].hasOwnProperty(key2) && !allKeysMatch) {
ownKeyCount2++;
if (key1 !== key1 && key2 !== key2) {// NaN check
allKeysMatch = true;// proven
break;
} else if (key1 === key2) {
if (a1[x][key1].toString() === a2[y][key2].toString()) {
allKeysMatch = true;// proven
console.log('KeyValueMatch=true : ' + key1 + ':' + a1[x][key1] + ' | ' + key2 + ':' + a2[y][key2]);
break;
}
}
}
}
if (ownKeyCount2 === 0) {// if second objects has no keys end early
console.log('-------------- End Check -------------');
return false;
}
// ---------------------------------------------
}
}
console.log('-------------- End Check -------------');
}
}
}
}
console.log('---------------------------------------');
if (noKeys || allKeysMatch) {
// If no keys in any objects, assume all objects are {} empty and so the same.
// If all keys match without errors, then all object match.
return true;
} else {
return false;
}
}
}
console.log('---------------------------------------');
return true;// one object
}
console.log('---------------------------------------');
return false;// no objects
} catch (e) {
if (typeof console !== 'undefined' && console.error) {
console.error('ERROR : Function ObjectCompare() : ' + e);
} else if (typeof console !== 'undefined' && console.warn) {
console.warn('WARNING : Function ObjectCompare() : ' + e);
} else if (typeof console !== 'undefined' && console.log) {
console.log('ERROR : Function ObjectCompare() : ' + e);
}
return false;
}
}
// TESTING...
var myObject1 = new Object({test: 1, item: 'hello', name: 'john', f: function(){var x=1;}});
var myObject2 = new Object({item: 'hello', name: 'john', test: 1, f: function(){var x=1;}});
var myObject3 = new Object({name: 'john', test: 1, item: 'hello', f: function(){var x=1;}});
// RETURNS TRUE
//console.log('DO ALL OBJECTS MATCH? ' + ObjectCompare(myObject1, myObject2, myObject3));
If you are using underscoreJs then you can simply use _.isEqual function
and it compares all keys and values at each and every level of hierarchy like below example.
var object = {"status":"inserted","id":"5799acb792b0525e05ba074c","data":{"workout":[{"set":[{"setNo":1,"exercises":[{"name":"hjkh","type":"Reps","category":"Cardio","set":{"reps":5}}],"isLastSet":false,"index":0,"isStart":true,"startDuration":1469689001989,"isEnd":true,"endDuration":1469689003323,"speed":"00:00:01"}],"setType":"Set","isSuper":false,"index":0}],"time":"2016-07-28T06:56:52.800Z"}};
var object1 = {"status":"inserted","id":"5799acb792b0525e05ba074c","data":{"workout":[{"set":[{"setNo":1,"exercises":[{"name":"hjkh","type":"Reps","category":"Cardio","set":{"reps":5}}],"isLastSet":false,"index":0,"isStart":true,"startDuration":1469689001989,"isEnd":true,"endDuration":1469689003323,"speed":"00:00:01"}],"setType":"Set","isSuper":false,"index":0}],"time":"2016-07-28T06:56:52.800Z"}};
console.log(_.isEqual(object, object1));//return true
If all the keys and values for those keys are same in both the objects then it will return true, otherwise return false.
Here is my attempt at validating JSON properties. I used #casey-foster 's approach, but added recursion for deeper validation. The third parameter in function is optional and only used for testing.
//compare json2 to json1
function isValidJson(json1, json2, showInConsole) {
if (!showInConsole)
showInConsole = false;
var aKeys = Object.keys(json1).sort();
var bKeys = Object.keys(json2).sort();
for (var i = 0; i < aKeys.length; i++) {
if (showInConsole)
console.log("---------" + JSON.stringify(aKeys[i]) + " " + JSON.stringify(bKeys[i]))
if (JSON.stringify(aKeys[i]) === JSON.stringify(bKeys[i])) {
if (typeof json1[aKeys[i]] === 'object'){ // contains another obj
if (showInConsole)
console.log("Entering " + JSON.stringify(aKeys[i]))
if (!isValidJson(json1[aKeys[i]], json2[bKeys[i]], showInConsole))
return false; // if recursive validation fails
if (showInConsole)
console.log("Leaving " + JSON.stringify(aKeys[i]))
}
} else {
console.warn("validation failed at " + aKeys[i]);
return false; // if attribute names dont mactch
}
}
return true;
}
I was wondering if there's more "functional" way to do this. Meaning, rather than having to do something like this:
let flag = true;
[1, 2, 3].forEach(n => if (n > 2) flag = false);
return flag;
I'm looking for something closer to this:
return [1, 2, 3].overEvery(n => n > 2);
The closest solution I've found is the Array.prototype.every function, but it terminates once it returns false, without iterating through the rest of the values in the collection.
Reason why I don't want to terminate is because I want to iterate through ALL fields in a form to display all the form errors before returning true or false in the form's onsubmit handler.
You can use Array.filter, which will return the elements that failed the validation if you return true for the elements that should fail.
Here is another approach:
function a()
{
var flag = true;
[1, 2, 3].forEach(function(value) { if(value > 2) flag = false; });
return flag;
}
console.log(a());
You can do a new prototype function that is like every but checks all the records:
if (!Array.prototype.everyCheckAll) {
Array.prototype.everyCheckAll = function(callbackfn, thisArg) {
'use strict';
var T, k;
if (this == null) {
throw new TypeError('this is null or not defined');
}
// 1. Let O be the result of calling ToObject passing the this
// value as the argument.
var O = Object(this);
// 2. Let lenValue be the result of calling the Get internal method
// of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
var len = O.length >>> 0;
// 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (typeof callbackfn !== 'function') {
throw new TypeError();
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 1) {
T = thisArg;
}
// 6. Let k be 0.
k = 0;
// 7. Repeat, while k < len
var oneFalse = false;
while (k < len) {
var kValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal
// method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal method
// of O with argument Pk.
kValue = O[k];
// ii. Let testResult be the result of calling the Call internal method
// of callbackfn with T as the this value and argument list
// containing kValue, k, and O.
var testResult = callbackfn.call(T, kValue, k, O);
// iii. If ToBoolean(testResult) is false, return false.
if (!testResult) {
oneFalse = true;
}
}
k++;
}
return !oneFalse;
};
}
function gt2(element, index, array) {
return element > 2;
}
document.write([1,2,3].everyCheckAll(gt2)); // false
document.write([4,5,6].everyCheckAll(gt2)); // true
Use Array#every, and check that every number is not greater than 2. Another advantage of Array#every is that whenever the callback returns false, the iteration stops, and the result is returned.
const flag = [1, 2, 3].every((n) => n <= 2);
console.log(flag);
Apologize in advance for lengthy post. I am trying to understand array reduce polyfill provided by MDN. I could not understand some lines in the polyfill can you explain it please. Below is the code
if (!Array.prototype.reduce) {
Object.defineProperty(Array.prototype, 'reduce', {
value: function(callback /*, initialValue*/) {
if (this === null) {
throw new TypeError( 'Array.prototype.reduce ' +
'called on null or undefined' );
}
if (typeof callback !== 'function') {
throw new TypeError( callback +
' is not a function');
}
// 1. Let O be ? ToObject(this value).
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// Steps 3, 4, 5, 6, 7
var k = 0;
var value;
if (arguments.length >= 2) {
value = arguments[1];
} else {
while (k < len && !(k in o)) {
k++;
}
// 3. If len is 0 and initialValue is not present,
// throw a TypeError exception.
if (k >= len) {
throw new TypeError( 'Reduce of empty array ' +
'with no initial value' );
}
value = o[k++];
}
// 8. Repeat, while k < len
while (k < len) {
// a. Let Pk be ! ToString(k).
// b. Let kPresent be ? HasProperty(O, Pk).
// c. If kPresent is true, then
// i. Let kValue be ? Get(O, Pk).
// ii. Let accumulator be ? Call(
// callbackfn, undefined,
// « accumulator, kValue, k, O »).
if (k in o) {
value = callback(value, o[k], k, o);
}
// d. Increase k by 1.
k++;
}
// 9. Return accumulator.
return value;
}
});
}
Question1: If you see step 1,
var o = Object(this);
I have checked both values of o and this, by passing an array to the polyfill method. There is no difference between o and this. They both are arrays(array.isarray returned true on both) with the same array value. Why not use below instead..?
var o = this;
Question2: Step 2
var len = o.length >>> 0;
Above line seems to right shift the o.length(32 bits). However no.of bits shifted is 0. So what advantage do we get by shifting 0 bits... Why not use below instead...?
var len = o.length;
Question 3: The first while condition inside else as below
while (k < len && !(k in o)) {
k++;
}
Initially k is set to 0 and it always seems to exist in o. So this while loop condition never gets to be true. So why do we need this while loop, if it never gets inside.
Question 1:
To make sure reduce is called on an object as reduce could be called through Function#call, Function#apply or even bound Function#bind:
Array.prototype.reduce.call(undefined, function() {});
So when accessing properties such as length, an error saying can't access property **** of undefined won't be thrown.
NOTE: the example above uses the native reduce which actually throws an error if not provided with an object.
Question 2:
To always have a valid integer value as length (even if it doesn't exist):
console.log(5 >>> 0); // 5
console.log(5.5 >>> 0); // 5
console.log("5" >>> 0); // 5
console.log("hello" >>> 0); // 0
console.log(undefined >>> 0); // 0
Question 3:
To deal with sparse arrays:
var arr = [5, 6];
arr[7000000] = 7;
arr.reduce(function(acc, v, i) {
console.log("index:", i);
}, 0);
It won't go through all the indices from 0 to 7000000, only those that really exist.
The following approach works :
var Arr = [1,2,3,4]
Array.prototype.myreduce = function(callback,initialvale){
var result ;
var resultvalue = initialvale
for(var key =0;key<this.length;key++){
var resultvalue =callback(resultvalue,this[key])
}
return resultvalue;
}
var customvalued = Arr.myreduce(function(total,value){
//console.log(total,value)
if(value%2==0){
total.push(value)
}
return total
},[])
console.log(customvalued)
In JavaScript, there are objects that pretend to be arrays (or are "array-like"). Such objects are arguments, NodeLists (returned from getElementsByClassName, etc.), and jQuery objects.
When console.logged, they appear as arrays, but they are not. I know that in order to be array-like, an object must have a length property.
So I made an "object" like this:
function foo(){
this.length = 1;
this[0] = "bar";
}
var test = new foo;
When I console log(test), I get (as expected) a foo object. I can "convert" it to an array using
Array.prototype.slice.call(test)
But, I don't want to convert it, I want it to be array-like. How do I make an array-like object, so that when it's console.logged, it appears as an array?
I tried setting foo.prototype = Array.prototype, but console.log(new foo) still shows a foo object, and not an array.
Depends specifically on the console. For custom objects in Chrome's developer console, and Firebug you'll need both the length and splice properties. splice will also have to be a function.
a = {
length: 0,
splice: function () {}
}
console.log(a); //[]
It's important to note, however, that there is no official standard.
The following code is used by jQuery (v1.11.1) internally to determine if an object should use a for loop or a for..in loop:
function isArraylike( obj ) {
var length = obj.length,
type = jQuery.type( obj );
if ( type === "function" || jQuery.isWindow( obj ) ) {
return false;
}
if ( obj.nodeType === 1 && length ) {
return true;
}
return type === "array" || length === 0 ||
typeof length === "number" && length > 0 && ( length - 1 ) in obj;
}
Note that it's possible to have an object that appears in the console as an array ([]) but that gets iterated over with a for..in loop in jQuery, or an object that appears as an object in the console ({}) but that gets iterated over with a for loop in jQuery.
The same question got into my mind as while we can use array like arguments parameter:
function arrayLike() {
console.log(typeof arguments)
console.log(arguments)
console.log(Array.from(arguments))
}
arrayLike(1,2,3)
So, let's try creating our own array-like object:
let arrayLikeObject = {
0: 1,
1: 2
}
console.log(Array.from(arrayLikeObject))
Obviously, there's no length property defined so our arrayLikeObject will only return an empty array. Now, let's try defining a length property:
let arrayLikeObject = {
length: 2,
0: 1,
1: 2
}
console.log(Array.from(arrayLikeObject))
What if length is set different?
let arrayLikeObject = {
length: 1,
0: 1,
1: 2
}
console.log(Array.from(arrayLikeObject))
// it will only return the value from first `0: 1`
let arrayLikeObject = {
length: 5,
0: 1,
1: 2
}
console.log(Array.from(arrayLikeObject))
// other 3 values will be printed as undefined
But, I don't want to convert it...
You actually wanted to create an array, not array-like object. The array-like object must be converted like you said:
Array.prototype.slice.call(arrayLikeObject)
// Or,
[].slice.call(arrayLikeObject)
If you do try to use array methods on array-like object, then you'll get type error:
let arrayLikeObject = {
length: 5,
0: 1,
1: 2
}
console.log(arrayLikeObject.sort())
Thus, to use the array methods on arrayLikeObject, we need to convert it into array as we did in preceding examples using Array.from.
Otherwise, you simply need to create an array:
let arr = [1,2] // I don't mean, you don't know
Other consideration:
You can't use it as constructor:
let arrayLikeObject = {
length: 1,
slice: function () {
return 1
}
}
console.log(new arrayLikeObject) // Type error
In the following snippet, the result will be [undefined] as the length property is set to 1 but there's no 0 indexed property:
let arrayLikeObject = {
length: 1,
slice: function () {
return 1
}
}
console.log(Array.from(arrayLikeObject))
But if you set the length to 0, then the result will be an empty array [] because we're telling that we don't have any values in this array-like object.
Is this any use: extended array prototype, seems like he's doing what you did and creating the prototype as an array, but including an extra method (that may or may not work, I've not tested this):
var MyArray = function() {
};
MyArray.prototype = new Array;
MyArray.prototype.forEach = function(action) {
for (var i = 0, l=this.length; i < l, ++i) {
action(this[i]);
}
};
Hope it helps in some way.
Look at this :
var ArrayLike = (function () {
var result;
function ArrayLike(n) {
for (var idx = 0; idx < n; idx++) {
this[idx] = idx + 1;
}
// this.length = Array.prototype.length; THIS WILL NOT WORK !
}
// ArrayLike.prototype.splice = Array.prototype.splice; THIS WILL NOT WORK !
// THIS WILL WORK !
Object.defineProperty(ArrayLike.prototype, 'length', {
get: function() {
var count = 0, idx = 0;
while(this[idx]) {
count++;
idx++;
}
return count;
}
});
ArrayLike.prototype.splice = Array.prototype.splice;
ArrayLike.prototype.multiple = function () {
for (var idx = 0 ; idx < this.length ; idx++) {
if (result) {
result = result * this[idx];
} else {
result = this[idx];
}
}
return result;
};
return ArrayLike
})();
var al = new ArrayLike(5);
al.__proto__ = ArrayLike.prototype;
console.log(al.length, al.multiple(), al);
This will display in Chrome : 5 120 [1, 2, 3, 4, 5]
I think this is what you are looking for.
Override the toString function.
foo.prototype.toString = function()
{
return "[object Foo <" + this[0] +">]";
}