I'm learning to program in Javascript and I'd like some help/clarification.
I declared an array that contains animal names. I defined a function that I use to split a string in two. Then I create an empty object literal and add an animal and corresponding breed. I'm trying to invoke the separateWords function in the object literal, but I need some clarification. Here's my code:
var myPets = ["Bengal Bobcat", "Beagle"];
var separateWords = function (string) {
return string.split(" ");
};
var nameCollection = {};
nameCollection.cat = separateWords(myPets[0]);
nameCollection.dog = myPets[1];
nameCollection.fish = null;
When I enter console.log(nameCollection) I get the following:
Object {cat: Array[2], dog: “Beagle”, fish: null}
cat: Array[2]
0: "Bengal"
1: "Bobcat"
length: 2
However, when I enter console.log( separateWords(myPets[0])), I see:
[“Bengal”, “Bobcat”]
I don’t understand why the value of cat shows up as Array[2].
The console displays it as Array[2] as it would be (potentially) unreadable if it expanded it fully. One way to see everything is to stringify it using JSON.stringify which goes through each item in the object recursively and calls toString() on it:
var myPets = ["Bengal Bobcat", "Beagle"];
var separateWords = function (string) {
return string.split(" ");
};
var nameCollection = {};
nameCollection.cat = separateWords(myPets[0]);
nameCollection.dog = myPets[1];
nameCollection.fish = null;
document.body.textContent = JSON.stringify(nameCollection);
You are assigning to cat the result of the separateWords() function call, passing myPets[0] as a parameter.
separateWords() returns an array and with the myPets[0] input it returns a new array with the "Bengal" and "Bobcat" values splitted by the whitespace.
The split() function is the one creating an array with the splitted values and this result is returned by your separateWords() function, which also is the value assigned to the cat object member.
Each browser implements its console like it wants.
So your browser decided to implement the behavior you describe.
If you don't like it, propose a better idea to the developers of this browser. Or use another browser.
I am going to assume you are using Chrome Developer Tools or Firebug.
Developer tools condenses arrays and objects into easily readable lines you then inspect further with. What I mean is, you push the little arrow next each line in the console log to further inspect each object. I will use pictures to explain this.
Here I am assigning an array and then assigning an element in an object to that array as so:
As you can see when I log the object it show's an Array[2] rather than expand the array. In this next picture I then expand the array to inspect it.
Why is this exactly? My first thought is ease of readability. If you have an app that is complex and you have numerous debugging console logs, you can see all the logs on single lines making it easier to hunt down specific logs. As well, if you have a very large and complex object, it is arguably easier to read all the root elements on each line without expanding all the objects and arrays found within that object recursively.
String.prototype.split() returns an array containing the two values in the string which have been split. Read through this.
nameCollection.cat = separateWords(myPets[0])[0]; // nameCollection.cat == Bengal
nameCollection.cat = separateWords(myPets[0])[1]; // nameCollection.cat == Bobcat
This is simply how javascript (and many other languages) work. When you try to print "nameCollection" javascript doesn't automatically do a nice job of printing the cat array. Instead, it simply prints some type related information, which in this case is saying "cat" is an array of length 2.
Related
I am trying to maintain the order of an array with mixed key types. The array contains mostly keys represented by string values -- but if you enter a numbered key it goes to the front. How can I force a key which is a number to be a string type?
E.g.
array = [];
array["one"] = "some data";
array["two"] = "some more data";
array["3"] = "this should not be the first element";
How can I make "3" a string type to prevent it from moving to the top of the index?
Oh wow did you ever open multiple cans of worms.
Javascript arrays are a special type of Javascript objects, and like all Javascript objects they can have arbitrary string properties:
const foo = [];
foo["bar"] = "hi";
However that string is a property of the array object, not an item in the array:
foo.forEach(console.log); // logs nothing
You can still access it like any other object property:
console.log(foo["bar"]); // "hi"
But it won't show up in the usual iterative constructs like c-style for loops or the map/forEach array methods.
The line in your example
array["3"] = "this should not be the first element";
is very different however, because of Javascript's playing fast and loose with type conversions this actually sets the string to the 4th slot in the array:
const bar = [];
bar["3"] = "oops!"; // equivalent to bar[3] = "oops!"
console.log(bar); // [empty x 3, "oops!"]
This piece of it is actually a good thing (other than the implicit conversion part) rather than a problem: sometimes you need a sparse array and JS supports those. Iterating it will only produce the one element:
bar.forEach((item, index) => console.log(item, index)); // ["oops", 3]
Note though that the string has the correct index of 3, and can be accessed that way even though there's nothing "in front" of it:
bar[3]; // "oops"
So the first two assignments in your example create properties on the array object, and the third assignment is the only one that actually adds an item to the array, at the 4th index (there's nothing at the first 3).
What you seem to want as Reese Casey suggests, is a plain object:
const foo = {}; // curly
foo["some string"] = "whatever";
However now the properties are basically unordered. If you want them to be in a guaranteed specific order you do want an array, but all your indicies will need to be integers, and should be sequential. You can achieve this easily by using the .push method:
foo = [];
foo.push("something");
foo.push("something else");
Now foo will have two elements, in the correct order, and index 0 and 1 respectively.
Update based on comment on the other answer:
I want some of the data to be ordered, and the rest of the data to follow
This can be accomplished through object destructuring:
const responseFromDB = {
oneKeyICareAbout: 3,
anotherKeyICareAbout: 2,
foo: 6,
bar: 7,
};
const {
oneKeyICareAbout,
anotherKeyICareAbout,
*rest,
} = responseFromDB;
const stuffToDisplay = [
oneKeyICareAbout,
anotherKeyICareAbout,
...Object.values(rest),
]; // [3, 2, 6, 7]
And at least the destructured stuff you put in the array will be ordered because by doing so you've ordered it.
Javascript arrays cannot have string indexes. This is actually working incorrectly as the index is adding a property to the array object.
Changing to an object makes more sense for this.
EDIT: Whilst below its mentioned you can have string indexes you are not actually using the array by doing so. The answer by Jared Smith goes into much more detail as to why.
The other answers explain what is happening with your array-object mixture. For having an indexable thing which can reproduce the original order, you can use a Map:
The Map object holds key-value pairs and remembers the original insertion order of the keys.
array = new Map();
array.set("one","some data");
array.set("two","some more data");
array.set("3","this should not be the first element");
console.log("Test of get:",array.get("two"));
console.log("Test of order:");
for(let entry of array)
console.log(entry);
I'm calling an external service and I get the returned domain object like this:
var domainObject = responseObject.json();
This converts the response object into a js object. I can then easily access a property on this object like this
var users = domainObject.Users
Users is a collection of key/value pairs like this:
1: "Bob Smith"
2: "Jane Doe"
3: "Bill Jones"
But CDT shows users as Object type and users[0] returns undefined. So how can I get a handle to the first item in the collection? I'm assuming that some type of type cast is needed but not sure how I should go about doing this
UPDATE
Here is one way I could access the values:
//get first user key
Object.keys(responseObject.json().Users)[0]
//get first user value
Object.values(responseObject.json().Users)[0]
But I need to databind through ng2 so I was hoping for a simpler way like this:
<div>
<div *ngFor="let user of users">
User Name: {{user.value}}
<br>
</div>
</div>
Maybe I should just create a conversion function in my ng2 component which converts the object into what I need before setting the databinding variable?
UPDATED ANSWER
So after scouring through a few docs I found the "newish" Object.entries() javascript function. You can read about it here. Pretty cool.
Anyways, give this a try. I am ashamed to say that I don't have time to test it, but it should get you going in the right direction.
usersArray = []
// Turn Users object into array of [key, value] sub arrays.
userPairs = Object.entries(users);
// Add the users back into an array in the original order.
for (i=0; i < userPairs; i++) {
usersArray.push(_.find(userPairs, function(userPair) { return userPair[0] == i }))
}
ORIGINAL ANSWER
I would use either underscore.js or lodash to do this. Both are super helpful libraries in terms of dealing with data structures and keeping code to a minimum. I would personally use the _.values function in lodash. Read more about it here.. Then you could use users[0] to retrieve the first item.
The only caveat to this is that lodash doesn't guarantee the iteration sequence will be the same as it is when the object is passed in.
users = _.values(users);
console.log(users[0]);
How about this:
let user= this.users.find(() => true)
This should return the "first" one.
If your initial object is just a plain object, how do you know it is sorted. Property members are not sorted, ie: looping order is nor guaranteed. I´d extract the user names into an array and the sort that array by the second word. This should work (as long as surnames are the second word, and only single spaces are used as separators).
var l=[];
for(var x in users) {
push.l(users[x]);
}
var l1=l.sort ( (a,b) => return a.split(" ")[1]<b.split(" ")[1]);
I have a variable called uids
var uids = [];
Then I write some value to it property
uids[16778923] = "3fd6335d-b0e4-4d77-b304-d30c651ed509"
But before it
if (!uids[user.id]) {
uids[user.id] = generateKey(user);
}
This thing behaves ok. If I try to get the value of it property
uids[currentUser.id]
It will give me a value of this property. If I try to call some methods like
Object.keys(uids);
It will give me, what I expected. And here the mystery comes...
uids;
RAM rest in piece. See the node eating ram
I am very confused now. What's wrong?
This is because you are creating a huge array and node will reserve memory for it - who knows what comes. I'd say that's a scenario where you would use a Map (or a plain object, but Map feels better here.
var uids = new Map();
var key = 456464564564654;
if (! uids.has(key)) {
uids.set(key, generateKey(user))
}
You are creating an empty array (length is zero), then you assign some value to an arbitrary index. This will make the array grow as big as the index and assign the value to that index. Look at this example using node.js REPL:
> var a = []
undefined
> a[5] = "something"
'something'
> a
[ , , , , , 'something' ]
> a.length
6
Instead of creating an array, you could create a Map() or an common javascript object (singleton). Javascript objects behave like Maps but only Strings can be used as keys. If you assign a Number to be key, javascript will convert it to String automatically.
Personally, I would go with objects because they perform better. Instantiating an object takes longer than instantiating a Map (and it doesn't seem like you need to create several groups of "uids"), but once done, adding new keys and retrieving values from any key in faster when using common objects. At least that's how things go in my node.js v6.7.0 on ubuntu 14.04 but you could try for yourself. And it would also make the least alteration to your code.
var uids = {} // common/ordinary empty javascript object instead of array.
if (!uids[user.id]) { // getting value from one key works the same.
uids[user.id] = generateKey(user) // assignment works the same.
}
////
uids[16778923] = "3fd6335d-b0e4-4d77-b304-d30c651ed509" // key will be "16778923".
uids[16778923] // getting value for key "16778923" can be done using 16778923 instead of "16778923".
////
uids[currentUser.id] // still returning values like this.
Object.keys(uids) // still returning an array of keys like this. but they are all Strings.
I've got code which splits the key and value at the = sign at end if line, the problem is that I have lines which have more then one =
For example:
user = aaaa
userb = bbbb
userc = test
userd = foo = bar = test
I read file with node fs and I divide it to the key and value at the = so userd causes a problem.
I am using var array = line.split('=');
Then I assign it to object in the loop
myobj = {}
myobj[array[0]] = array[1];
This is working (my obj in the loop contains all the users) until I get to userD.
So how can I concatenate all the values of userD to myObj
If we assume that everything after the first = sign is the value, regardless of whether theres another = sign then you can do this:
myobj[array[0]] = array.slice(1).join(' = ');
Now, if you want to store it simple as an array of equal values, you can do:
myobj[array[0]] = array.slice(1);
The slice function returns the same array starting at the element passed (it does more but thats what it's for here).
Update I didn't think of this answer but it's even a bit faster!
Have a look at Array.prototype.shift():
myobj[array.shift()] = array;
Unlike Array.prototype.splice(), shift() won't create an entirely new array in order to wrap up the one value we're trying to extract. See my comment here.
Here are the results from a performance test; it shows operations per second (higher is better):
UserAgent shift slice splice
Chrome 44.0.2403 4,290,938 3,993,798 1,042,423
Chrome 46.0.2468 3,631,277 4,020,712 1,098,866
Firefox 38.0 1,706,068 1,419,219 895,714
Firefox 40.0 1,739,110 1,508,623 955,794
IE 11 in Compatibility Mode 10.0.0 3,393,698 2,855,417 1,721,229
Total 14,761,091 13,797,769 5,714,026
According to these results, shift() is the fastest with slice() right behind. splice(), on the other hand, is a good bit slower than both... mainly because it's ill-suited to this particular task.
That said, I was surprised. I expected this answer to be the fastest.
Use splice function:
myobj[array.splice(0, 1)] = array;
This will cut the first item out of your array and return it as myobj parameter, the rest is going to be kept in the array, so you can either use array as an object property or let's say join it to a string:
myobj[array.splice(0, 1)] = array.join(", ");
I'm using a specific game making framework but I think the question applies to javascript
I was trying to make a narration script so the player can see "The orc hits you." at the bottom of his screen. I wanted to show the last 4 messages at one time and possibly allow the player to look back to see 30-50 messages in a log if they want. To do this I set up and object and an array to push the objects into.
So I set up some variables like this initially...
servermessage: {"color1":"yellow", "color2":"white", "message1":"", "message2":""},
servermessagelist: new Array(),
and when I use this command (below) multiple times with different data called by an event by manipulating servermessage.color1 ... .message1 etc...
servermessagelist.push(servermessage)
it overwrites the entire array with copies of that data... any idea why or what I can do about it.
So if I push color1 "RED" and message1 "Rover".. the data is correct then if I push
color1"yellow" and message1 "Bus" the data is two copies of .color1:"yellow" .message1:"Bus"
When you push servermessage into servermessagelist you're really (more or less) pushing a reference to that object. So any changes made to servermessage are reflected everywhere you have a reference to it. It sounds like what you want to do is push a clone of the object into the list.
Declare a function as follows:
function cloneMessage(servermessage) {
var clone ={};
for( var key in servermessage ){
if(servermessage.hasOwnProperty(key)) //ensure not adding inherited props
clone[key]=servermessage[key];
}
return clone;
}
Then everytime you want to push a message into the list do:
servermessagelist.push( cloneMessage(servermessage) );
When you add the object to the array, it's only a reference to the object that is added. The object is not copied by adding it to the array. So, when you later change the object and add it to the array again, you just have an array with several references to the same object.
Create a new object for each addition to the array:
servermessage = {"color1":"yellow", "color2":"white", "message1":"", "message2":""};
servermessagelist.push(servermessage);
servermessage = {"color1":"green", "color2":"red", "message1":"", "message2":"nice work"};
servermessagelist.push(servermessage);
There are two ways to use deep copy the object before pushing it into the array.
1. create new object by object method and then push it.
servermessagelist = [];
servermessagelist.push(Object.assign({}, servermessage));
Create an new reference of object by JSON stringigy method and push it with parse method.
servermessagelist = [];
servermessagelist.push(JSON.parse(JSON.stringify(servermessage));
This method is useful for nested objects.
servermessagelist: new Array() empties the array every time it's executed. Only execute that code once when you originally initialize the array.
I also had same issue. I had bit complex object that I was pushing in to the array. What I did; I Convert JSON object as String using JSON.stringify() and push in to the Array.
When it is returning from the array I just convert that String to JSON object using JSON.parse().
This is working fine for me though it is bit far more round solution.
Post here If you guys having alternative options
I do not know why a JSON way of doing this has not been suggested yet.
You can first stringify the object and then parse it again to get a copy of the object.
let uniqueArr = [];
let referencesArr = [];
let obj = {a: 1, b:2};
uniqueArr.push(JSON.parse(JSON.stringify(obj)));
referencesArr.push(obj);
obj.a = 3;
obj.c = 5;
uniqueArr.push(JSON.parse(JSON.stringify(obj)));
referencesArr.push(obj);
//You can see the differences in the console logs
console.log(uniqueArr);
console.log(referencesArr);
This solution also work on the object containing nested keys.
Before pushing, stringify the obj by
JSON.stringify(obj)
And when you are using, parse by
JSON.parse(obj);
As mentioned multiple times above, the easiest way of doing this would be making it a string and converting it back to JSON Object.
this.<JSONObjectArray>.push(JSON.parse(JSON.stringify(<JSONObject>)));
Works like a charm.