Can anyone explain how the result of RegExp.prototype.exec is made?
If you try something like that: /d/g.exec("d is a character, dd") the result is an array structured as explained here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec
My question is, how the array has non-indexeed properties like index and input?
Normally an array is a collection indexeed by integers ([0...n]);
var re = /d/g;
var str = "domains are always domains";
var result = re.exec(str);
console.log("typeof result", typeof result);
console.log("Array.isArray(result)", Array.isArray(result));
console.log("result.length", result.length);
console.log("result", result);
An array is just an object. In fact, you can make simple array-like objects just like that:
var fakeArr = {}
fakeArr[0] = 'foo';
fakeArr[1] = 'bar';
fakeArr.length = 2;
The only thing that distinguishes an array from a plain object is the behaviour of the .length property and the various array-specific methods on Array.prototype.
Related
Are there any substantial reasons why modifying Array.push() to return the object pushed rather than the length of the new array might be a bad idea?
I don't know if this has already been proposed or asked before; Google searches returned only a myriad number of questions related to the current functionality of Array.push().
Here's an example implementation of this functionality, feel free to correct it:
;(function() {
var _push = Array.prototype.push;
Array.prototype.push = function() {
return this[_push.apply(this, arguments) - 1];
}
}());
You would then be able to do something like this:
var someArray = [],
value = "hello world";
function someFunction(value, obj) {
obj["someKey"] = value;
}
someFunction(value, someArray.push({}));
Where someFunction modifies the object passed in as the second parameter, for example. Now the contents of someArray are [{"someKey": "hello world"}].
Are there any drawbacks to this approach?
See my detailed answer here
TLDR;
You can get the return value of the mutated array, when you instead add an element using array.concat[].
concat is a way of "adding" or "joining" two arrays together. The awesome thing about this method, is that it has a return value of the resultant array, so it can be chained.
newArray = oldArray.concat[newItem];
This also allows you to chain functions together
updatedArray = oldArray.filter((item) => {
item.id !== updatedItem.id).concat[updatedItem]};
Where item = {id: someID, value: someUpdatedValue}
The main thing to notice is, that you need to pass an array to concat.
So make sure that you put your value to be "pushed" inside a couple of square brackets, and you're good to go.
This will give you the functionality you expected from push()
You can use the + operator to "add" two arrays together, or by passing the arrays to join as parameters to concat().
let arrayAB = arrayA + arrayB;
let arrayCD = concat(arrayC, arrayD);
Note that by using the concat method, you can take advantage of "chaining" commands before and after concat.
Are there any substantial reasons why modifying Array.push() to return the object pushed rather than the length of the new array might be a bad idea?
Of course there is one: Other code will expect Array::push to behave as defined in the specification, i.e. to return the new length. And other developers will find your code incomprehensible if you did redefine builtin functions to behave unexpectedly.
At least choose a different name for the method.
You would then be able to do something like this: someFunction(value, someArray.push({}));
Uh, what? Yeah, my second point already strikes :-)
However, even if you didn't use push this does not get across what you want to do. The composition that you should express is "add an object which consist of a key and a value to an array". With a more functional style, let someFunction return this object, and you can write
var someArray = [],
value = "hello world";
function someFunction(value, obj) {
obj["someKey"] = value;
return obj;
}
someArray.push(someFunction(value, {}));
Just as a historical note -- There was an older version of JavaScript -- JavaScript version 1.2 -- that handled a number of array functions quite differently.
In particular to this question, Array.push did return the item, not the length of the array.
That said, 1.2 has been not been used for decades now -- but some very old references might still refer to this behavior.
http://web.archive.org/web/20010408055419/developer.netscape.com/docs/manuals/communicator/jsguide/js1_2.htm
By the coming of ES6, it is recommended to extend array class in the proper way , then , override push method :
class XArray extends Array {
push() {
super.push(...arguments);
return (arguments.length === 1) ? arguments[0] : arguments;
}
}
//---- Application
let list = [1, 3, 7,5];
list = new XArray(...list);
console.log(
'Push one item : ',list.push(4)
);
console.log(
'Push multi-items :', list.push(-9, 2)
);
console.log(
'Check length :' , list.length
)
Method push() returns the last element added, which makes it very inconvenient when creating short functions/reducers. Also, push() - is a rather archaic stuff in JS. On ahother hand we have spread operator [...] which is faster and does what you needs: it exactly returns an array.
// to concat arrays
const a = [1,2,3];
const b = [...a, 4, 5];
console.log(b) // [1, 2, 3, 4, 5];
// to concat and get a length
const arrA = [1,2,3,4,5];
const arrB = [6,7,8];
console.log([0, ...arrA, ...arrB, 9].length); // 10
// to reduce
const arr = ["red", "green", "blue"];
const liArr = arr.reduce( (acc,cur) => [...acc, `<li style='color:${cur}'>${cur}</li>`],[]);
console.log(liArr);
//[ "<li style='color:red'>red</li>",
//"<li style='color:green'>green</li>",
//"<li style='color:blue'>blue</li>" ]
var arr = [];
var element = Math.random();
assert(element === arr[arr.push(element)-1]);
How about doing someArray[someArray.length]={} instead of someArray.push({})? The value of an assignment is the value being assigned.
var someArray = [],
value = "hello world";
function someFunction(value, obj) {
obj["someKey"] = value;
}
someFunction(value, someArray[someArray.length]={});
console.log(someArray)
I have a code :
var index = 100;
var arr =[];
arr[index.toString()] = "Hello"
The result : index still known as integer not a string. Anyone can explain what's wrong with my code?
You have to declare associative arrays using {}, which creates a new object, because in JavaScript, arrays always use numbered indexes.
You need to declare an object: var arr={};
arrays use numbered indexes.
objects use named indexes.
var index = 100;
var arr ={};
arr[index.toString()] = "Hello";
console.log(arr);
How to make associative array with number as string in Javascript
JavaScript doesn't have associative arrays in the sense that term is frequently used. It has objects, and as of ES2015 (aka "ES6"), it has Maps.
The result : index still known as integer not a string. Anyone can explain what's wrong with my code?
The index variable's value is still a number, yes, because you haven't done anything to change it. But the index in the array is a string (and would be even if you didn't use .toString()), because standard arrays aren't really arrays at all1, they're objects with special handling of a class of properties (ones whose names are strings that fit the spec's definition of an array index), a special length property, and that use Array.prototype as their prototype.
Here's proof that array indexes are strings:
var a = [];
a[0] = "zero";
for (var name in a) {
console.log("name == " + name + ", typeof name == " + typeof name);
}
That said, you don't want to use an array when you want a generic object or map.
Here's using a generic object for name/value mappings:
var o = Object.create(null);
var name = "answer";
o[name] = 42;
console.log(o[name]); // 42
The property names in objects are strings or (as of ES2015) Symbols. I used Object.create(null) to create the object so it wouldn't have Object.prototype as its prototype, since that gives us properties (toString, valueOf, etc.) that we don't want if we're using the object as a map.
Here's using a Map:
var m = new Map();
var name = "answer";
m.set(name, 42);
console.log(m.get(name)); // 42
The main advantages Maps have over objects are:
Their keys can be anything, not just strings or Symbols
They're iterable, so you can use for-of to loop through the mappings they contain
Maps have a size property telling you how many entries they have
Maps guarantee that iteration of their entries is performed in the order the entries were added to the map
With ES6, you could use a Map, which holds any type as key.
var map = new Map;
map.set(100, "Hello");
map.set('100', "Foo");
console.log(map.get(100)); // 'Hello'
console.log(map.get('100')); // 'Foo'
console.log([...map]);
JavaScript does not support arrays with named indexes, in JavaScript, arrays always use numbered indexes.
If you use a named index, JavaScript will redefine the array to a standard object.
After that, all array methods and properties will produce incorrect results.
As you can see in the following example:
var person = [];
person["firstName"] = "John";
person["lastName"] = "Doe";
person["age"] = 46;
var x = person.length; // person.length will return 0
console.log(x);
var y = person[0]; // person[0] will return undefined
console.log(y);
I'm calling the .string method on a string and setting that equal to another variable. It returns an array with the [match value, index, and input].
When I try to refer to the second element in the array, it comes back as undefined. Can someone tell me why that is? Heres my code:
var str = "hello world"
var matchArray = str.match("ell");
=>matchArray = ["ell",index:1,input:"hello world"]
var index = matchArray[1];
console.log(index);
=>undefined
Thanks in advance.
var str = "hello world"
var matchArray = str.match("ell");
matchArray is an Array but in javascript as we know we can set properties in array as well as it's an object.
In the above case, matchArray has only the mathces in the array. but the other properties such as index and input are in the object.
If you do console.dir(matchArray) you would get the properties as well.
So to access those properties use object's notation like matchArray.index or matchArray.input
JavaScript is an interesting language. Although the matchArray object is indeed an array, in JS you can add new members to any object. So, matchArray is an array of length 1, but it also has the members index and input defined on it. Try this:
...
console.log(matchArray.index);
console.log(matchArray.input);
We could define a similar object from scratch like this:
var hybridObj = [ "ell" ]; // It's an array
hybridObj.index = 1; // With props
hybridObj.input = "hello world";
This question already has answers here:
Copy array by value
(39 answers)
Closed 8 years ago.
I have an array example fruit . I'd like to copy it as array fruits2, without keeping reference.
As in the following example reference is kept so fruits is modified.
var fruit = function (name){
this.name = name;
}
var fruits = [];
fruits.push(new fruit('apple'));
fruits.push(new fruit('banana'));
fruits.push(new fruit('orange'));
var fruits2 = fruits;
fruits2.length = 0;
console.log(fruits);
http://jsfiddle.net/vkdqur82/
Using JSON.stringify and JSON.parse does the trick but the objects in fruits2 are not any longer of type fruit but are of general type object
var temp = JSON.stringify(fruits);
var fruits2 = JSON.parse(temp);
I would like to know an alternative approach which would keep inner object of fruit.
Use slice: var fruits2 = fruits.slice(); should do it.
Your jsFiddle, modified
See also: MDN
**Edit. I was a bit lazy, let's correct my answer to make up for that.
For an Array of just values slice is perfect. For an Array of objects or arrays or a mix of values/objects/arrays, the Array and Object elements of the Array to clone need cloning too. Otherwise they will be references to the original arrays or objects (so: not copies) and a change of one [of these references of arrays or objects] will be reflected in all 'clones' containing a reference to it.
To clone an Array of Arrays/Objects/mixed values Array.map is your friend. There are several methods to think of:
creating a new instance with old data
var fruits1 = fruits.map(function(v) {return new Fruit(v.name);});
using JSON
var fruits2 = fruits.map(function(v) {return JSON.parse(JSON.stringify(v));});
create and use some cloning method
var fruits3 = fruits.map(function(v) {return cloneObj(v);});
In case 3, a method for cloning could look like:
function cloneObj(obj) {
function clone(o, curr) {
for (var l in o){
if (o[l] instanceof Object) {
curr[l] = cloneObj(o[l]);
} else {
curr[l] = o[l];
}
}
return curr;
}
return obj instanceof Array
? obj.slice().map( function (v) { return cloneObj(v); } )
: obj instanceof Object
? clone(obj, {})
: obj;
}
Using this cloneObj method, Array.map is obsolete.
You can also use var fruitsx = cloneObj(fruits);
The jsFiddle from the link above is modified to demonstrate these methods.
For Array.map, see again MDN
slice can do the trick.
You can also use .map but .slice is normally faster.
var copy = fruits.map(function(item) {return item});
Hope it helps
You can declare a new array and use concat method, so that you concat all values from your array to the new array. Something like this:
var x = ["a","b"];
var a = [];
a = a.concat(x);
console.log(a);
I edited my poor answer.
Best regards.
Is there a way for this line to always work and not throw TypeError: Cannot read property 'Whatever' of undefined
var MyArray = [];
MyArray[StringVariableName][StringVariableName2].push("whatever");
Try this:
var MyArray = [];
MyArray[StringVariableName] = MyArray[StringVariableName] || [];
MyArray[StringVariableName][StringVariableName2] = MyArray[StringVariableName][StringVariableName2] || [];
MyArray[StringVariableName][StringVariableName2].push("whatever");
You could even, through the power of expressions, do this with a one-liner.
(MyArray[StringVariableName][StringVariableName2] || (MyArray[StringVariableName][StringVariableName2] = [])).push("whatever");
You could use the literal syntax to set things up like you'd have them:
var myObj = {
StringVariableName: {
StringVariableName2: []
}
};
myObj.StringVariableName.StringVariableName2.push("whatever");
I think instead of using array in the first place, use object if your keys are not integers.
In Javascript Arrays are also object So it is not wrong to do this
var a = [];
a['key'] = 'something';
console.log(a); //Gives []
I think it is conceptually wrong So instead of using Array to hold such pair of data you should use objects. See this:
var myObject = myObject || {};
myObject[str1] = myObject[str1] || {};
myObject[str1][str2] = myObject[str][str2] || [];
// Now myObject[str1][str2] is an array. Do your original operation
myObject[str1][str2].push("whatever");
To check without getting an error:
this snippet allows you to check if a chained object exists.
var x;
try{x=MyArray[name1][name2][name3][name4]}catch(e){}
!x||(x.push('whatever'));
from
https://stackoverflow.com/a/21353032/2450730
Shorthand creation of object chains in Javascript
this function allows you to create chained objects with a simple string.
function def(a,b,c,d){
c=b.split('.');
d=c.shift();//add *1 for arrays
a[d]||(a[d]={});//[] for arrays
!(c.length>0)||def(a[d],c.join('.'));
}
usage
var MyArray={};//[]
def(MyArray,'name1.name2.name3.name4');//name1+'.'+name2....
from
https://stackoverflow.com/a/21384869/2450730
both work also for arrays with a simple change.replace {} with []
if you have any questions just ask.