Use underscore.js "pluck" on the observed knockout array

I have an observable array of objects and I want to wrest 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 wrest the values โ€‹โ€‹inside the object, not the whole observable.

Can I do this with just one command?

I tried:

_.pluck(myArray(), "id()") and _.pluck(myArray(), "id"())

But they return an array of undefined values โ€‹โ€‹and "id is not a function" respectively.

Thanks!

+7
javascript arrays
source share
4 answers

Short answer

Use _.invoke instead of _.pluck

See this sample script .

Long answer

_.pluck(list, propertyName) works as documented:

A convenient version of perhaps the most common use case for a map: retrieving a list of property values.

Or, as best deduced on lodash docs: _.pluck(collection, path)

Gets the value of the path property of all elements in the collection.

So if you do this:

 _.pluck(myArray(), "id") 

what you get is an array with all id . And all these id are observable, as in the objects of the original array

But you can use _.invoke(list, methodName, *arguments) , which, as described:

Calls a method named MethodName for each value in the list. Any additional arguments passed to the call will be redirected to the method call.

or, in the version of lodash _.invoke(collection, path, [args])

Calls a method along the path for each item in the collection, returning an array of the results of each called method. Any additional arguments are provided for each method invoked. If methodName is a function, it is called for this element and is associated with this every element in the collection.

This way you execute each observable and get its value as expected:

 _.invoke(myArray(), "id") 

Pay attention to models with full observables!

The first comment on this question made me turn on this notification:

The best solution is to use ko.toJS to convert all observables into a view model into a regular JavaScript object with regular properties. Once you do this, an underscore or any other library will work as expected.

The _.invoke solution _.invoke works for one level observable, like this case. If there were several levels of nested observables, it would completely fail, because it calls a function at the end of the path, and not at every step of the path, for example, _.invoke will not work for this case:

 var advices = [{ person: ko.observable({ name = ko.observable('John') }), advice: ko.observable('Beware of the observables!') }]; 

In this case, you can only use _.invoke in the first level, for example:

 var sentences = _.invoke(advices,'advice'); 

But this did not work:

 var names = _.invoke(advices,'person.name'); 

In this call, only name will be called, but person not, therefore it will not work because person is observable, therefore it does not have a name property.

NOTE: lodash is another library, similar and mostly compatible with underscores, but better in some aspects.

+4
source share

I was able to solve this using the "map" function:

 _.map(myArray(), function(item) {return item.id()}); 

But I was hoping to use pluck, as this is an exact use case for this type of scenario.

+2
source share

Because name is a function, what about pluck your source array into an array of functions, and then use ko.toJS to convert to a 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"] 
+2
source share

Expand first

 _.pluck(ko.toJS(myArray), 'id') _(ko.toJS(myArray)).pluck('id) 
+1
source share

All Articles