Is there a way to do this out of the box with the _.sortBy method or any other part of the library?
You mean like this?
var array = [
{ name: "banana" },
{ name: "carrot" },
{ name: "apple" }
];
var sorted = _(array).sortBy("name");
I'd say it works out of the box.
If you wanted to sort an ordinary array of strings, you probably just want to use sort:
var flatArray = ["banana", "carrot", "apple"];
flatArray.sort();
See here. Also works.
Note that Underscore's sortBy returns a new array which is sorted, where JavaScript's built-in sort function sorts an array in place.
Since you tagged your question with the backbone.js tag, I'm assuming you mean to sort a collection, you just need to provide a comparator function on your collection and backbone will keep the collection sorted.
If your question is specifically alphabeticical sorting, I believe that is the default sort, from the backbone.js documentation (I linked to it above)
chapters.comparator = function(chapter) {
return chapter.get("page");
};
Related
I have an observable array of objects and I want to pluck out the values using underscore.js
For example:
ko.observableArray([{
id: ko.observable(1),
name: ko.observable("name1")
},
{
id: ko.observable(2),
name: ko.observable("name2")
},
...])
And I just want to pluck the values inside of the object rather than the whole observable.
Can I do this with just one command?
I tried:
_.pluck(myArray(), "id()") and _.pluck(myArray(), "id"())
But these return an array of undefineds and "id is not a function" respectively.
Thanks!
Short answer
Use _.invoke instead of _.pluck
See this sample fiddle.
Long answer
_.pluck(list, propertyName) works as documented:
A convenient version of what is perhaps the most common use-case for map: extracting a list of property values.
Or, as better exlained on lodash docs: _.pluck(collection, path)
Gets the property value of path from all elements in collection.
So, if you do this:
_.pluck(myArray(), "id")
what you get is an array with all the id's. And all of these id's are observables, as in the objects of the original array
But you can use _.invoke(list, methodName, *arguments), which, as documented:
Calls the method named by methodName on each value in the list. Any extra arguments passed to invoke will be forwarded on to the method invocation.
or, on lodash version _.invoke(collection, path, [args])
Invokes the method at path on each element in collection, returning an array of the results of each invoked method. Any additional arguments are provided to each invoked method. If methodName is a function it is invoked for, and this bound to, each element in collection.
In this way, you execute each observable, and get its value as expected:
_.invoke(myArray(), "id")
Mind the viewmodels full of observables!
The first comment to this question has made me include this notice:
The best solution is using ko.toJS to convert all the observables in a view model into a regular JavaScript object, with regular properties. Once you do it, underscore, or any other library, will work as expected.
The _.invoke solution only works for a single level of observables, as this case. If there were several level of nested observables, it will completely fail, because it invokes a function at the end of the path, not at each step of the path, for example, _.invoke wouldn't work for this case:
var advices = [{
person: ko.observable({
name = ko.observable('John')
}),
advice: ko.observable('Beware of the observables!')
}];
In this case, you could only use _.invoke on the first level, like this:
var sentences = _.invoke(advices,'advice');
But this wouldn't work:
var names = _.invoke(advices,'person.name');
In this call, only name would be invoked, but person not, so this would fail, because person is an observable, thus it doesn't have a name property.
NOTE: lodash is another library similar, and mostly compatible with underscore, but better in some aspects
I was able to solve this by using the "map" function:
_.map(myArray(), function(item) {return item.id()});
But I was hoping to use pluck since it's the exact use-case for this type of scenario.
Because name is a function, how about pluck your original array into an array of functions, then using ko.toJS to convert it into string array?
var myArray = ko.observableArray([{
id: ko.observable(1),
name: ko.observable("name1")
},
{
id: ko.observable(2),
name: ko.observable("name2")
}]);
var names = _.pluck(myArray(), 'name');
console.log(ko.toJS(names)); // Output: ["name1", "name2"]
Unwrap it first
_.pluck(ko.toJS(myArray), 'id')
_(ko.toJS(myArray)).pluck('id)
I'm a student and new to Angular, so I apologize if this is obvious.
I see Angular used frequently for displaying lots of data. From what I've seen, it works fantastically when your data is stored as an array, but only puts up a fight when you try to use it with an associative array (Possible, but requires work-arounds).
Scenario:
I've got an array of kittens:
var kittens = [
{
id: "id0",
breed: "persian",
name: "Mr. Cuddles"
},
{
id: "id1",
breed: "siamese",
name: "Puddin' Face"
},
{
id: "id2",
breed: "ragamuffin",
name: "Rumblemuffins"
}
];
And there are a lot of kittens. I have an angular factory kittenService that retrieves this data from an API and maintains this array. I need the ability to look up these kittens by id and filter by breed. I turned this into an associative array with id as the key, but then I run into issues with $filter, etc. There isn't as much documentation for associative arrays, and it all seems just easier to implement using regular arrays.
tl;dr
If AngularJS is used to act on data, Why are associative arrays not as common in AngularJS applications? Should I think differently about data storage when using Angular?
You cannot refer to (what you are calling an array but in reality are objects aka hashes) associative arrays by index and you cannot do things like check the length property. My guess is it is much more efficient to work on actual arrays than to pretend that object hashes are arrays.
When I running this code by node app.js
'use strict';
var data = {"456":"First","789":"Second","123":"Third"};
console.log(data);
I've geting next result:
{ '123': 'Third', '456': 'First', '789': 'Second' }
Why JS or Node.js (I don't know who) sort this object by keys? I don't want this.
What you're seeing is a behavior of the V8 JavaScript engine. It's not something specified by the JavaScript specification, and it's not something you can rely on (most engines don't do that).
But the important thing here is: There is no order to a JavaScript object. So V8's behavior is just as correct as putting those keys in any other order. If you want things in a particular order, you have to use an ordered container, like an array.
JavaScript objects aren’t ordered. If you want things in order, you can use an array.
var data = [
{ key: "456", value: "First" },
…
];
Or, alternatively, keep the key order in a separate array.
Properties in an object aren't ordered. You can't expect the order you give to be preserved when a representation of your object is built for the console.
The iteration order is implementation dependent. Don't rely on it. If you need some order, use another data structure (for example and array of key-values).
// try this to sort employees by age
employees.sort(function(a, b){
return a.age-b.age
});
Example
Link: http://jsfiddle.net/ewBGt/
var test = [{
"name": "John Doo"
}, {
"name": "Foo Bar"
}]
var find = 'John Doo'
console.log(test.indexOf(find)) // output: -1
console.log(test[find]) // output: undefined
$.each(test, function(index, object) {
if(test[index].name === find)
console.log(test[index]) // problem: this way is slow
})
Problem
In the above example I have an array with objects. I need to find the object that has name = 'John Doo'
My .each loop is working, but this part will be executed 100 times and test will contain lot more objects. So I think this way will be slow.
The indexOf() won't work because I cannot search for the name in object.
Question
How can I search for the object with name = 'John Doo' in my current array?
jQuery $.grep (or other filtering function) is not the optimal solution.
The $.grep function will loop through all the elements of the array, even if the searched object has been already found during the loop.
From jQuery grep documentation :
The $.grep() method removes items from an array as necessary so that
all remaining items pass a provided test. The test is a function that
is passed an array item and the index of the item within the array.
Only if the test returns true will the item be in the result array.
Provided that your array is not sorted, nothing can beat this:
var getObjectByName = function(name, array) {
// (!) Cache the array length in a variable
for (var i = 0, len = test.length; i < len; i++) {
if (test[i].name === name)
return test[i]; // Return as soon as the object is found
}
return null; // The searched object was not found
}
I have done sometimes "searchable map-object" in this kind of situation. If the array itself is static, you can transform in to a map, where array values can be keys and map values indexes. I assume values to be unique as in your example.
Lo-Dash (www.lodash.com) has create selection of utils for easily looping etc. Check it out!
Note: But often you really don't have to worry about looping trough array with 100 elements.
If you just want to find out if the value is there, you can use lodash's includes function like this:
var find = 'John Doo'
[{ "name": "John Doo" }, { "name": "Foo Bar" }].some(function (hash) {
if (_.includes(hash, find)) return true;
});
Documentation:
_.includes()
Array.prototype.some()
Perhaps you should use the $.grep functionality in jQuery:
var test = [{
"name": "John Doo"
}, {
"name": "Foo Bar"
}]
var find = 'John Doo'
var found = $.grep(test, function(obj){
return obj['name'] == find;
});
console.log(found);
Fiddle: http://jsfiddle.net/ewBGt/3/
The only thing you can possibly do is use build-in array methods (if available) in preference over doing the looping yourself – the filter method would be applicable here.
But I expect that JS libraries like jQuery used by sbeliv01 in his answer already check for that internally (and provide a fallback solution if these array methods are not available natively) – so don’t expect a massive performance boost.
i want to sort the keys or name of the method.. following is the object http://pastie.org/411854
If I understand correctly, you would like to put members in order based on key or method name (which is the same value)? This has no meaning for Object since members have no order.
However, if you put these objects in an array like this:
var apiDocs = [
{
"methodName": "friendsGetByUser",
"methodDescription": "Returns user ids of friends of the specified user.",
...
}, {
"methodName": "friendsGetBestFriends",
"methodDescription": "Returns user ids of best friends of the logged in user.",
...
}
...
];
Then you can sort the array easily by invoking Array.sort passing in compare function.
apiDocs.sort(function (a, b) {
return a.methodName < b.methodName;
});
To get a sorted array of the function names you could use the following:
var functions = [];
for(functionName in apiDocs) {
functions.push(functionName);
}
functions.sort();
If you want to perform some sort of more complex sort you could do something along the lines of
var functions = [];
for(functionName in apiDocs) {
functions.push(apiDocs[functionName]);
}
functions.sort(//put sorting closure here)
You can get more information about custom sorting here: Array Sorting Explained