I am trying to understand the difference between map and pluck transformational operators in RxJS.
Can anyone help me with this?
The docs say
Pluck : Like map, but meant only for picking one of the nested
properties of every emitted object.
Therefore, let's say you have
[{ name: 'Joe', age: 30, job: { title: 'Developer', language: 'JavaScript' },
{ name: 'Sarah', age: 35 }]
and you want a list of all job titles.
Using map would be kind of a pain (because of the nullability of job), but with 'pluck' you can write pluck('job', 'title') and it will traverse the tree looking for job.title - and won't fail if job is null.
Example taken from : https://www.learnrxjs.io/operators/transformation/pluck.html
https://jsfiddle.net/btroncone/n592m597/
As #mgm87 said, you can perform an operation with map.
On the opposite, pluck is just taking a value.
For example, with map you could do something like that:
this.http.get('...some api url to get a user...')
.map(response => response.json())
.map(user => user.age > 18 ? 'major': 'minor')
.do(isMajorOrMinor => console.log(isMajorOrMinor))
So you can manipulate your data down the chain even conditionally.
BUT, for me one of the big differences is that map is typed.
Which means if you have some data let say:
interface IUser {
name: string;
age: number;
dogs: IDog[];
}
And you receive at some point a user, from which you want to get his dogs:
user$
.map(user => user.dogs)
.do(dogs => ...) // here, you do NOT need to precise (dogs: IDog[]) because Typescript will make a type inference
And that's why I'm always using map even to just "pluck" some data.
Stop using pluck!
Pluck is now planned to be removed in RxJS v8.
Do you know what is the reason?
Because after the addition of the optional chaining operator in JS, it's essentially, just a weak version of Map.
So what's the difference between the two?
Both are used to "transform" the data that is going to be emitted.
Map can be used to... map an observable emission (like we do in JS with Array.prototype.map), while Pluck is used to select/pick a property to emit (without having to emit properties that we don't care for, hence improving the performance).
But even before the optional chaining operator, you could just map the properties instead of plucking them. The result & performance were/are about the same.
pluck('prop')
is just a shorthand for:
map(x => x.prop)
Well, then what was the reason behind the implementation of Pluck?
It was basically implemented to achieve path traversal safety, meaning you could try to pluck a nested property without getting the error (that Map would throw) if the property is not defined:
pluck('foo', 'bar', 'baz'); // no error if foo is not defined
map(x => x.foo.bar.baz) // error if foo is not defined
With optional chaining, this advantage doesn't exists anymore, since we can just do this:
map(x => x?.foo?.bar?.baz)
This is the main reason why the Pluck operator is going to be deprecated and removed in the future.
Another important reason is TS typing for pluck is quite complex and not as robust as the map operator.
Got that info in GitHub:
The commit of Pluck deprecation is almost one year old but I still don't see any warning of future deprecation in the docs, hence I am posting here since I think it's a good thing to know. I already stopped plucking for this reason.
Map can perform an operation on each emitted value.
http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-map
https://www.learnrxjs.io/operators/transformation/map.html
// value from observable = 10
map(x => 10*x)
// value from map = 100
Pluck simply picks one of the nested properties of each emitted value.
http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-pluck
https://www.learnrxjs.io/operators/transformation/pluck.html
// value from observable = {p = 10, w = 100}
pluck('p')
// value from pluck = 10
They are very similar, but as I understand it, map works with an array whereas pluck takes the values from an object.
This is the place to go for specifics.
Related
I was originally searching how to return several actions in a ngrx effect, and found I need to return an array of actions.
Then I noticed that returning a simple array in the switchMap works as fine as returning an observable created from this array.
For example:
timer(1000).pipe(switchMap(val => from([val, val + 1)])).subscribe(val => console.log(val));
timer(1000).pipe(switchMap(val => [val, val + 1])).subscribe(val => console.log(val));
I expect the first to work and think it is the correct syntax.
I don't expect the second to work but it actually does and I would like to understand why.
Thanks,
Because switchMap, among other flattening-operators (mergeMap, exhaustMap, ...), takes an ObservableLike as the return type of its projection function.
An ObservableLike can be Observable, Promise or Array. If you provide an array, it is converted into a stream of its items - basically the same as if you had used from.
I am attempting to populate an array of tuples in a for-loop. The array needs to be predefined.
I am trying to do something along the following lines:
for class in keys(classes)
arr[class]=pmap(y->func(arg,y),1:length(arg1),batch_size=Int(round(length(arg)/nworkers())))
end
In the specific case, classes is a dictionary of type Dict{String,Tuple{Int64,Int64}}. For e.g. classes=Dict("Item1" => (5000,10000), "Item2" => (5000,10000))
The type-definition of broadcasting operation pmap(...) when class is Item1 is an Array{Tuple{Float64,Float64,Float64,Array{Float64,1}},1}. What is an appropriate way of preallocating arr?
arr[Item1] will be of type Array{Tuple{Float64,Float64,Float64,Array{Float64,1}},1}. So, I presume arr would have to defined as an Array{Array{Tuple{Float64,Float64,Float64,Array{Float64,1}},1}}, or something to this extent. But, I couldn't come up with the right notation for defining this.
It seems like I have overthought this; defining arr as a Dict{String,Array{Tuple{Float64,Float64,Float64,Array{Float64,1}},1}} was helpful.
function c() {
return Math.random();
}
source$.pipe(
map(a => c())
).subscribe(v => console.log(v));
Say there's a simple code like above. What I tried was logging the value when the source stream emits something but obviously, the value I log has nothing to do with the value from the source stream. So it got me considering using mapTo operator like this:
function c() {
return Math.random();
}
source$.pipe(
mapTo(c())
).subscribe(v => console.log(v));
But as you may guess, the value is always the same. More accurately speaking, it stays as the first value which is not what I want.
So my point is, I want the evaluation to be executed each time the source emits a value which I don't use at the evaluation. I can get it working like the first code by using map operator but it just doesn't seem right to use map when I don't use the value from the source stream. Is it okay to use map like this? Or is there any workaround for this kind of matter using mapTo or something else? Any insight would be appreciated!
According to the official definition, mapTo emits the given constant value on the output Observable every time the source Observable emits a value.
Therefore the behavior you described is the expected one. The first evaluation from Math.random() is kept and emitted for every time.
There seems nothing wrong to use map here to get the random values as you expect.
I am attempting to do some string manipulation in rjxs, and while I can accomplish it with the built in behaviors on the string class and the array class in Javascript, I'm wanting to use this as an exercise to learn even more about rxjs and understand a fluent code-flow better.
To that end, it's imperative to me that I discover a way to do it that can fit in a fluent solution, instead of a series of variable assignments like I see in most rxjs examples.
Essentially, here's the situation; I've got a string of text;
const example = `
key || value
key || value
key || value
value
value
value
key || key[key1] = value | key[key2] = value
key || value
`;
The first thing that I need to do is use string.split('\n') to create an array of strings, so that I can through each line and perform further operation.
example.string.split('\n') does give the desired results, but trying to send this into rxjs begins to get rather mixed yield. With the pipe method, I know that I send the results into rxjs as an Observable, but I'm having a really troubling time grasping how to truly treat it from there without excessive nesting into the map operator.
For example, if I do ...
of(example.string.split('\n')).pipe(
map(results => results.toString().split('||')),
map(results => ... ),
...
).subscribe();
I can start to get a semblance of what I'm looking for, but what I'd really like to do is ...
of(example).pipe(
split('\n'),
split('||'),
concatMap(results => ...)
).subscribe();
Reading the documentation on lettable operators, seen here, it looks like this should be a pretty easy thing to create. In theory, it should look like this in my mind;
const split = (separator: string) => <T>(source: Observable<T>) =>
new Observable(observer => {
source.subscribe({
next(x) { observer.next(x.toString().split(separator)); },
error(err) { observer.error(err); },
complete() { observer.complete(); }
})
});
So that should make the whole code obvious enough;
of(example).pipe(
split('\n')
).subscribe(result => console.log(`[n]::${result}`));
But this doesn't give me what I really expect. I expected to get an array of the lines, but if I output it, I get ...
[n]::, key || value, key || value, key || value, ,
value, value, , value, key || key[key1] = value |
key[key2] = value, key || value,
I'm really unclear what I'm doing wrong, here. Since it's hard to demonstrate rxjs in most of the code playgrounds like plunkr or jsfiddle, at least to my knowledge, I've prepared a playground environment to demonstrate my work on stackblitz, if it helps.
You'll find all of the pertinent code in the playground/index.ts file. I've done the best I can to abstract away the need to have any knowledge of angular, as I've painstakingly earmarked the sections that should be left alone to make it continue showing output on the right side. If you do not know angular, but can help with rxjs, you should be able to work without ever disturbing that setup.
STACKBLITZ PLAYGROUND
Your code is working fine, just the es6 template string ${} flattened your array into a string. If you console.dir or log the result, you will see a correct array retrieved.
is there a way to make Puppet to differentiate between
my_custom_type { 'key':
value => 'blah',
}
and
my_custom_type { 'key':
value => ['blah'],
}
when declaring resource attributes?
this is for a custom type, so i have full ruby-land control, but both show up to Puppet::Type#set_parameters and consequently Puppet::Property#should= as 'blah'.
i'm using Puppet 3.4.3 on top of Ruby 2.0.0 (through Boxen). i'm not sure how easy it would be for me to change either of those versions.
CONTEXT: the custom type I'm implementing edits Apple property lists (.plist files), where a string and an array containing a single string element are quite different.
declaring the property like
newproperty(:value, :array_matching => :all) do
along the lines of
https://docs.puppetlabs.com/guides/custom_types.html#customizing-behaviour
doesn't seem to change what set_parameters or should= receive, they just make Puppet::Property#should return ['blah'] instead of 'blah' in both cases. it appears the differentiation is tossed out further up at the parser level.
providing
my_custom_type { 'key':
value => [['blah']],
}
doesn't help either - same result.
PLEASE NOTE:
i realize i can work around this by providing additional information in the declaration, like so:
my_custom_type { 'key':
value => ['blah'],
is_array => true,
}
or
my_custom_type { 'key':
value_array => ['blah'],
}
i'm wondering if there is a way to capture whether an array or scalar was declared... though feel free to explain to me why doing so would be unwise or heretic in Puppet-world; i'm a little new to this strange place.
The underlying single-element special-casing was deprecated in puppet3, and is not part of the language's behaviour since a long while. See https://tickets.puppetlabs.com/browse/PUP-1299.