I have a collection of users which I just got from a JSON server response. Assume the following:
var users = [
{id: 1, name: 'john', surname: 'smith'},
{id: 2, name: 'john', surname: 'smith'},
{id: 3, name: 'john', surname: 'smith'},
{id: 4, name: 'john', surname: 'smith'},
{id: 5, name: 'john', surname: 'smith'},
{id: 6, name: 'john', surname: 'smith'},
{id: 7, name: 'john', surname: 'smith'}
];
At some points in the business logic and the (handlebars) templates of the application I need to have the full name of each user but of course I don't want to repeat the concatenation logic of name + ' ' + surname all over the place.
Thus I do the following which "enhances" those users and adds this function I want:
for(var i=0,length=users.length; i<length; i++) {
users[i].getFullName = function() {
return this.name + ' ' + this.surname;
}
}
This technique works fine but my questions are:
is there a better way of doing this? Note that I want an object oriented style solution and not something like getFullNameOfUser(user).
I can measure the time penalty to add the function to all those objects, but can I measure the memory penalty for adding the function to the objects? Solutions such as JavaScript object size do not count functions.
you could create a class, Users, and add getFullNameOfUser to the prototype.
var User = function(obj){
this.id = obj.id;
this.name = obj.name;
this.surname = obj.surname;
};
User.prototype.getFullNameOfUser = function(){
return this.name + ' ' + this.surname;
};
for(var i=0,length=users.length; i<length; i++) {
users[i] = new User(users[i]);
}
About your question in the comment, you could do:
var User = function(obj){
for(var key in obj){
this[key] = obj[key];
}
};
but this way you might end up with unwanted properties in the object
This seems inefficient because you are adding a function to each user.
Using handlebars.js you should be able to simply handle concatenation in the view, i.e.:
<h2>Welcome {{user.name}} {{user.surname}}!</h2>
You could always create a bunch of utility functions and call them with a specific context as needed.
function getFullName () {
return this.name + ' ' + this.surname;
};
getFullName.call(users[0]);
Related
I'm decoding an object and so far I got it working. Let's say I have this object:
var person = [{
firstname: "Mike",
lastname: "123ñññ"
age: 20
}]
So in order to decode ñ and render ñ, I'm simply doing this:
new DOMParser().parseFromString(person[0].lastname, "text/html").documentElement.textContent;
and this will render the value
ñññ
which is what I want, so it will look like this:
lastname: "ñññ"
However, the issue that I'm facing is that I need to decode values for each property in the object because I may get those special characters for firstname or other properties. So my question is how to decode property values on an object assuming that the object may look like this:
var person = [{
name: "Mike",
lastname: "123ñññ"
age: 20,
employeer: {
name: 'ABC Company ñê',
supervisors:[
{
name: 'Steveä',
code: 'è468'
}
]
}
}]
NOTE:
I don't need help on decoding that values of each property on my object, since I'm already doing that, I just need to come up with a recursive function that will do that on a nested object
I think a recursive decode using DOMParser is a good idea. Here's an in-place transformer. Perform a deep copy first and then transform in-place if you prefer.
var person = [{
name: "Mike",
lastname: "123ñññ",
age: 20,
employer: {
name: 'ABC Company ñê',
supervisors: [
{
name: 'Steveä',
code: 'è468'
}
]
}
}];
console.log(person);
function htmlDecode(input)
{
var doc = new DOMParser().parseFromString(input, "text/html");
return doc.documentElement.textContent;
}
function fix(obj) {
for (let prop in obj) {
switch (typeof obj[prop]) {
case 'object':
fix(obj[prop]);
break;
case 'string':
obj[prop] = htmlDecode(obj[prop]);
break;
}
}
}
fix(person);
console.log(person);
in functional languages there are libraries to recursively walk tree scructures. In clojure there is the zipper and walk for example.
you could write it yourself but it will quickly get complicated so I suspect using JSON.stringify + parse would give you what you need. Both functions take second argument that's replacer and reviver respectively that allow you to intercept the transformations and alter the values.
Here is example from the official documentation:
function replacer(key, value) {
// Filtering out properties
if (typeof value === 'string') {
return undefined;
}
return value;
}
var foo = {foundation: 'Mozilla', model: 'box', week: 45, transport: 'car', month: 7};
JSON.stringify(foo, replacer);
// '{"week":45,"month":7}'
Try this:
function decodeObject(obj){
if(typeof obj == 'object'){
var text = JSON.stringify(obj),
div = document.createElement('div');
div.innerHTML = text;
obj = JSON.parse(div.childNodes[0].nodeValue);
}
return obj;
}
var person = decodeObject([{
name: "Mike",
lastname: "123ñññ",
age: 20
}]);
console.log(person);
I want to understand how the below code works in EmberJS?
Ember.$.map($this.get('myMap'), function(entitlements, id) {
// What is entitlements & id here & what should be $this.get('myMap')?
})
is this conventional/standard JS syntax ?
Any examples would be great ?
Ember.$ with lead you to jQuery, so you're using jQuery's map method.
Essentially what it does it call a function for each element in the array, allowing you to "map" that value to another and return it to be added to a new array. For instance:
If you have an array of javascript objects like var names = [{ name: 'John', age: 12}, {name: 'Fred', age: 14}] and you wanted to extract all names to a new array you could do:
var names = [{ name: 'John', age: 12}, {name: 'Fred', age: 14}];
var result = Ember.$.map(names, function(instance, index) {
return instance.name
})
console.log(result) //Would print ['John', 'Fred'];
You could do all sort of things like return new objects to be added to the array.
I'm trying to loop over an Array using Underscore, I have this:
var friends = [
{
name: 'Jimmy',
age: 21
},
{
name: 'Anna',
age: 19
},
{
name: 'Alina',
age: '22'
},
{
name: 'Carl',
age: '22'
}
];
var names = _(friends).pluck('name');
for(i = 0; i < friends.length; i++){
$('ul').append('<li>' + names[i] + '</li>');
}
<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul></ul>
I'm trying to do the same using underscore's each method, but I can't achieve it, I'm trying doing something like this:
_(friends).each(function(namessss, i){
$('ul').append('<li>' + names[i] + '</li>');
});
This actually works, but what I don't understand is why?! What's the first parameter for, it doesn't matter what I write there, it works and I wonder how to do this, and why this way works, thanks.
According to the documentation:
Each invocation of iteratee is called with three arguments: (element, index, list). If list is a JavaScript object, iteratee's arguments will be (value, key, list)
Your code works no matter what you write for that first argument, because you never actually use that argument (the value itself). Instead you just reference an index on the names array which you have defined outside of this loop.
If you'd like to actually use the value in the each loop, you can use something like this:
_(friends).each(function(friend, i){
$('ul').append('<li>' + friend.name + '</li>');
});
this would be a reasonably typical way :-
var friends = [
{
name: 'Jimmy',
age: 21
},
{
name: 'Anna',
age: 19
},
{
name: 'Alina',
age: '22'
},
{
name: 'Carl',
age: '22'
}
];
var names = _(friends).pluck('name');
_.each(names, function(name){
$('ul').append('<li>' + name + '</li>');
});
<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul></ul>
The each method has the syntax...
_.each(list, callbackFunc);
where callbackFunc has the syntax...
function callbackFunc(element, index, list) {}
Your code just happens to work because you stored the name values in the names array which you are then accessing using the index passed to your callback function. A better way to write the code would be...
_(friends).each(function(friend){
$('ul').append('<li>' + friend.name + '</li>');
});
I am trying to pluck multiple attributes from a Backbone collection but it returns undefined.
collection
{
id:1,
name:"raju",
age:23,
sex:male,
hobbies:..
}
{
id:2,
name:"ramesh",
age:43,
sex:male,
hobbies:..
}
... //many models
I am trying to get multiple attributes from collection.
collection.pluck(["id","name","age","sex"]);
Expected output
[{//multiple attributes},{}]
is there any alternative way to get multiple attributes?
As #elclanrs said, collection.pluck extracts a single attribute, you will have to use _.map with a custom extraction function. Something like
var c = new Backbone.Collection([
{id: 1, name: "raju", age: 23, sex: "male"},
{id: 2, name: "ramesh", age: 43, sex: "male"}
]);
var plucked = c.map(function (model) {
return _.pick(model.toJSON(), ["name", "age"]);
});
console.log(plucked);
And a demo http://jsfiddle.net/U7p9u/
You could simplify this call by combining Collection.invoke and Model.pick
var c = new Backbone.Collection([
{id: 1, name: "raju", age: 23, sex: "male"},
{id: 2, name: "ramesh", age: 43, sex: "male"}
]);
plucked = c.invoke("pick", ["name", "age"]);
console.log(plucked);
http://jsfiddle.net/U7p9u/5/
In a similar spirit, if your extraction function is defined on the prototype of your models:
var M = Backbone.Model.extend({
mypluck: function () {
return this.pick("name", "age");
}
});
var C = Backbone.Collection.extend({
model: M
});
var c = new C([
{id: 1, name: "raju", age: 23, sex: "male"},
{id: 2, name: "ramesh", age: 43, sex: "male"}
]);
var plucked = c.invoke("mypluck");
console.log(plucked);
http://jsfiddle.net/U7p9u/3/
In the docs it says:
"[pluck is the] Equivalent to calling map and returning a single
attribute from the iterator."
This leads me to believe that it isn't possible with multiple properties because you're basically replacing one item in the collection with one of its properties. So basically you're doing this:
var collect = [{a:'foo',b:'baz'},{a:'lol',b:'fur'}];
var key = 'a';
var result = collect.map(function(o){ return o[key] });
A possible solution would be to return an array and then flatten it, something like this:
result = [].concat.apply([],collect.map(function(o){ return [o.a,o.b]; }));
console.log(result); //=> ["foo", "baz", "lol", "fur"]
http://jsfiddle.net/CoryDanielson/Lj3r85ew/
You could add select methods to collections and models.
(or name it whatever you feel is appropriate)
/**
Backbone Model Select/Multi-get -------------------------------------------
*/
Backbone.Model.prototype.select = function(props) {
if ( arguments.length > 1 ) {
props = slice.call(arguments);
}
if ( _.isArray(arguments[0]) ) {
props = arguments[0];
}
// If requesting multiple, return all props
if ( _.isArray(props) ) {
return _.object(props, _.map(props, _.bind(this.get, this)));
}
// Else, return single property
return this.get(props);
}
/**
Backbone Collection Select ------------------------------------------------
*/
Backbone.Collection.prototype.select = function(props) {
if ( arguments.length > 1 ) {
props = slice.call(arguments);
}
if ( _.isArray(arguments[0]) ) {
props = arguments[0];
}
return _.map(this.models, function(m) {
return m.select(props);
});
}
This would allow you to select multiple properties across all models of a collection, or select multiple properties on a model.
collection.select('id', 'first', 'last'); // or ['id', 'first', 'last']
model.select('first', 'last'); // or ['first', 'last']
If I have an array of strings, I can use the .join() method to get a single string, with each element separated by commas, like so:
["Joe", "Kevin", "Peter"].join(", ") // => "Joe, Kevin, Peter"
I have an array of objects, and I’d like to perform a similar operation on a value held within it; so from
[
{name: "Joe", age: 22},
{name: "Kevin", age: 24},
{name: "Peter", age: 21}
]
perform the join method only on the name attribute, to achieve the same output as before.
Currently I have the following function:
function joinObj(a, attr){
var out = [];
for (var i = 0; i < a.length; i++){
out.push(a[i][attr]);
}
return out.join(", ");
}
There’s nothing wrong with that code, it works, but all of a sudden I’ve gone from a simple, succinct line of code to a very imperative function. Is there a more succinct, ideally more functional way of writing this?
If you want to map objects to something (in this case a property). I think Array.prototype.map is what you're looking for if you want to code functionally.
(fiddle)
If you want to support older browsers, that are not ES5 compliant you can shim it (there is a polyfill on the MDN page above). Another alternative would be to use underscorejs's pluck method:
var users = [
{name: "Joe", age: 22},
{name: "Kevin", age: 24},
{name: "Peter", age: 21}
];
var result = _.pluck(users,'name').join(",")
Well you can always override the toString method of your objects:
var arr = [
{name: "Joe", age: 22, toString: function(){return this.name;}},
{name: "Kevin", age: 24, toString: function(){return this.name;}},
{name: "Peter", age: 21, toString: function(){return this.name;}}
];
var result = arr.join(", ");
console.log(result);
On node or ES6+:
users.map(u => u.name).join(', ')
I've also come across using the reduce method, this is what it looks like:
console.log(
[
{name: "Joe", age: 22},
{name: "Kevin", age: 24},
{name: "Peter", age: 21}
]
.reduce(function (a, b) {
return (a.name || a) + ", " + b.name}
)
)
The (a.name || a) is so the first element is treated correctly, but the rest (where a is a string, and so a.name is undefined) isn't treated as an object.
Edit: I've now refactored it further to this:
x.reduce(function(a, b) {return a + ["", ", "][+!!a.length] + b.name;}, "");
which I believe is cleaner as a is always a string, b is always an object (due to the use of the optional initialValue parameter in reduce)
Edit 6 months later: Oh what was I thinking. "cleaner". I've angered the code Gods.
I don't know if there's an easier way to do it without using an external library, but I personally love underscore.js which has tons of utilities for dealing with arrays, collections etc.
With underscore you could do this easily with one line of code:
_.pluck(arr, 'name').join(', ')
lets say the objects array is referenced by the variable users
If ES6 can be used then the easiest solution will be:
users.map(user => user.name).join(', ');
If not, and lodash can be used so :
_.map(users, function(user) {
return user.name;
}).join(', ');
const lists = [
{name: "Joe", age: 22},
{name: "Kevin", age: 24},
{name: "Peter", age: 21}
]
const joinedStr = lists.map((list) => list.name).join(" ")
console.log('joined',joinedStr)
This should do it since map will return an array of strings and you can join it then
If object and dynamical keys: "applications\":{\"1\":\"Element1\",\"2\":\"Element2\"}
Object.keys(myObject).map(function (key, index) {
return myObject[key]
}).join(', ')
An old thread I know but still super relevant to anyone coming across this.
Array.map has been suggested here which is an awesome method that I use all the time.
Array.reduce was also mentioned...
I would personally use an Array.reduce for this use case.
Why? Despite the code being slightly less clean/clear. It is a much more efficient than piping the map function to a join.
The reason for this is because Array.map has to loop over each element to return a new array with all of the names of the object in the array. Array.join then loops over the contents of array to perform the join.
You can improve the readability of jackweirdys reduce answer by using template literals to get the code on to a single line. "Supported in all modern browsers too"
// a one line answer to this question using modern JavaScript
x.reduce((a, b) => `${a.name || a}, ${b.name}`);
not sure, but all this answers tho they work but are not optiomal since the are performing two scans and you can perform this in a single scan. Even though O(2n) is considered O(n) is always better to have a true O(n).
const Join = (arr, separator, prop) => {
let combined = '';
for (var i = 0; i < arr.length; i++) {
combined = `${combined}${arr[i][prop]}`;
if (i + 1 < arr.length)
combined = `${combined}${separator} `;
}
return combined;
}
This might look like old school, but allows me to do thig like this:
skuCombined = Join(option.SKUs, ',', 'SkuNum');
you can convert to array so get object name
var objs = [
{name: "Joe", age: 22},
{name: "Kevin", age: 24},
{name: "Peter", age: 21}
];
document.body.innerHTML = Object.values(objs).map(function(obj){
return obj.name;
});
This worked for me:
var users = [
{name: "Joe", age: 22},
{name: "Kevin", age: 24},
{name: "Peter", age: 21}
]
return users.map((user: { name: string; }) => user.name).join(", ");
try this
var x= [
{name: "Joe", age: 22},
{name: "Kevin", age: 24},
{name: "Peter", age: 21}
]
function joinObj(a, attr) {
var out = [];
for (var i=0; i<a.length; i++) {
out.push(a[i][attr]);
}
return out.join(", ");
}
var z = joinObj(x,'name');
z > "Joe, Kevin, Peter"
var y = joinObj(x,'age');
y > "22, 24, 21"
Easiest way:
const oldArrayOfObjects = [
{name: "Bob", age:40},
{name: "Andrew", age:25},
{name: "Peter", age:30}];
const newArrayOfStringNames = oldArrayOfObjects.map((OA) => OA.name);
const newArrayOfAges = oldArrayOfObjects.map((OA) => OA.age);
console.log({newArrayOfStringNames, newArrayOfAges})