I have this Rxjs testing code. It fail deliberately, because i want to show you the failing log. Which i found hard to understand, or at least i cannot read it fluently.
Someone can explain me what means : $[i].frame = i' to equals i'' ?
import { delay } from 'rxjs/operators';
import { TestScheduler } from 'rxjs/testing';
describe('Rxjs Testing', () => {
let s: TestScheduler;
beforeEach(() => {
s = new TestScheduler((actual, expected) => {
expect(actual).toEqual(expected);
});
});
it('should not work', () => {
s.run(m => {
const source = s.createColdObservable('-x-y-z|');
const expected = '-x-y-z|'; // correct expected value is '---x-y-z|'
const destination = source.pipe(delay(2));
m.expectObservable(destination).toBe(expected);
});
});
});
To help you better understand what is going on with the output, let's first try to follow statements from the console. There is a link that points at where the error has happened. It's at 10th line of code which is this line:
expect(actual).toEqual(expected);
Setting breakpoint to this line and running the test in debug mode reveals actual and expected objects.
actual values are (represented in JSON format):
[
{
"frame": 3,
"notification": {"kind": "N", "value": "x", "hasValue": true}
},
{
"frame": 5,
"notification": {"kind": "N", "value": "y", "hasValue": true}
},
{
"frame": 7,
"notification": {"kind": "N", "value": "z", "hasValue": true}
},
{
"frame": 8,
"notification": {"kind": "C", "hasValue": false}
}
]
And the expected:
[
{
"frame": 1,
"notification": {"kind": "N", "value": "x", "hasValue": true}
},
{
"frame": 3,
"notification": {"kind": "N", "value": "y", "hasValue": true}
},
{
"frame": 5,
"notification": {"kind": "N", "value": "z", "hasValue": true}
},
{
"frame": 6,
"notification": {"kind": "C", "hasValue": false}
}
]
Comparing the two arrays, you can see that frame properties are different for each object of the same index. This weird output comes from Jasmine's toEqual function, so let's try to understand it based on the values from above. This line from the console
Expected $[0].frame = 3 to equal 1.
means that expected value of 1 is not 1, but is actually 3. This part $[0].frame = 3 suggests what the actual value is, and this to equal 1 is what you as developer think it should be. I.e. expected[0].frame (which is 1) is not equal to actual[0].frame (which is 3). And so on, expected[1].frame is not equal to actual[1].frame...
Now, you may wonder why do you even get such values for actual and expected. This is explained much more in detail on the official docs. When you create cold observable with this marble diagram -x-y-z| and delay it with 2 units, it becomes ---x-y-z| which is then transformed to something comparable - an actual array. The first three - signs indicate three empty, non-emitting frames. They are at the positions 0, 1 and 2. They do not have representations in any of the two arrays.
Then comes the first real value x. It is represented as the first object in actual array (actual[0]). x is at position 3 thus the frame property has the same value. notification property has some meta data like the value of the emitted item. You can conclude values for y and z the same way.
Side note: when using run() method, frames become values of 1, 2, 3, etc. instead of 10, 20, 30 when not using run(), for legacy reasons.
Related
I would like to do the following:
Send a request to a server and retrieve details of animals available in a petstore.
post /getAnimalStatus response:
"animals": [
{
"animalId": "567839",
"gender": "Female",
"age": 2,
"isEligibleAsPet": true,
"animalStatus": "AVAILABLE"
},
{
"animalId": "648562",
"gender": "Male",
"age": 3,
"isEligibleAsPet": true,
"animalStatus": "AVAILABLE"
},
{
"animalId": "965895",
"gender": "Female",
"age": 5,
"isEligibleAsPet": false,
"animalStatus": "UNAVAILABLE"
}
]
}
I would then like to:
Search for all animals which are UNAVAILABLE in the response and store the array value in a variable.
Send a request to a server to update all unavailable animals' statuses to 'AVAILABLE'
This request will be formatted as below where we are using the array value from the response above in the animal object below.:
put /updateAnimals
"animal": {
"animalId": "965895",
"gender": "Female",
"age": 5,
"isEligibleAsPet": false,
"animalStatus": "UNAVAILABLE"
},
"updateStatus": "AVAILABLE"
}
use JsonPath (or karate.filter()) to do it: https://github.com/intuit/karate#jsonpath-filters
use a second feature file and call to iterate: https://github.com/intuit/karate#data-driven-features
Also look at this answer for ideas: https://stackoverflow.com/a/60358697/143475
Please ask a more specific question to get a more specific answer.
Case
I have a json object (as imported from excell file, and it needs to be formatted like one as well afterwards). So i can not mess with the structure at all.
The input consists of a json formatted object with sheets, every sheet is an array of objects.
I want to add another object (the var 'append') to the array (as last item) when the sheetnumber equals the kwadrantcode in the 'append' object
input 1
The payload
{
"(sheet)1": [
{
"kwadrantId": 1.0,
"Team": "blauw1",
"timestamp": "2019-09-26T16:37:54",
"controlecode": 12431
}
],
"(sheet)2": [
{
"kwadrantId": 2.0,
"Team": "rood1",
"timestamp": "2019-09-26T16:37:54",
"controlecode": 81243
},
{
"kwadrantId": 2.0,
"Team": "blauw2",
"timestamp": "2019-09-26T18:00:54",
"controlecode": 67676
}
]
}
the code so far
%dw 2.0
output application/json
var append = {"kwadrant": "2",
"controlecode": "22222",
"kleur": "blauw",
"subteam": "1",
"form_id": "83177",
"submit": "Claim"
}
---
payload pluck($) map (sheet, sheetnumber)-> (sheet map (claim, claimcounter) -> claim) ++ [append]
This code succesfully appends the object to the array at the right location.
The Problem here is that this last part (the map target) does not seem to allow a conditional.
So it will always insert it at the end of every list, not just the one where i want it to end up.
[
[
{
"kwadrantId": 1.0,
"Team": "blauw1",
"timestamp": "2019-09-26T16:37:54",
"controlecode": "12431"
},
{
"kwadrant": "2",
"controlecode": "22222",
"kleur": "blauw",
"subteam": "1",
"form_id": "83177",
"submit": "Claim"
}
],
[
{
"kwadrantId": 2.0,
"Team": "rood1",
"timestamp": "2019-09-26T16:37:54",
"controlecode": "12431"
},
{
"kwadrantId": 2.0,
"Team": "blauw2",
"timestamp": "2019-09-26T18:00:54",
"controlecode": 67676.0
},
{
"kwadrant": "2",
"controlecode": "22222",
"kleur": "blauw",
"subteam": "1",
"form_id": "83177",
"submit": "Claim"
}
]
]
A few things i have tried
honestly, i think i've tried everything the past two hours, but here are a few i can think of out of the top of my head.
++ [append] if (true)
(if true )++ [append]
payload pluck($) map (sheet, sheetnumber)-> (sheet map (claim, claimcounter) -> claim)((if true) ++ [append])
and most variations i can think of with brackets
I will worry about formatting the 'append' var to the right structure later, just getting it in the right spot is the issue i can't seem to get working.
The simplest way that I can think of is to use the mapObject function
%dw 2.0
output application/json
var append =
{
"kwadrant": "2",
"controlecode": "22222",
"kleur": "blauw",
"subteam": "1",
"form_id": "83177",
"submit": "Claim"
}
fun extractNumber(pageName: Key) =
(pageName as String match /\(sheet\)([0-9]+)/)[1]
---
payload mapObject ((value, key, index) -> do {
if(extractNumber(key) == append.kwadrant)
{(key): value << append}
else
{(key): value}
})
So basically I will go for every sheet in the root and when it matches the one I need to add append it to the value using the <<
I have an array of object like below ,
[
{
"myValues": []
},
{
"myValues": [],
"values": [
{"a": "x", "b": "1"},
{"a": "y", "b": "2"}
],
"availableValues": [],
"selectedValues": []
}
]
also if i iterate the object, the "values" key present in the object, it will convert it into like below,
[
{
"myValues": []
},
{
"myValues": [],
"values": [
{"a": "x", "b": "1"},
{"a": "y", "b": "2"}
],
"availableValues": [
{"a": "x", "b": "1"},
{"a": "y", "b": "2"}
],
"selectedValues": ["x", "y"]
}
]
I tried with ramda functional programming but no result,
let findValues = x => {
if(R.has('values')(x)){
let availableValues = R.prop('values')(x);
let selectedValues = R.pluck('a')(availableValues);
R.map(R.evolve({
availableValues: availableValues,
selectedValues: selectedValues
}))
}
}
R.map(findValues, arrObj);
Immutability
First of all, you need to be careful with your verbs. Ramda will not modify your data structure. Immutability is central to Ramda, and to functional programming in general. If you're actually looking to mutate your data, Ramda will not be your toolkit. However, Ramda will transform it, and it can do so in a relatively memory-friendly way, reusing those parts of your data structure not themselves transformed.
Fixing your approach
Let's first look at cleaning up several problems in your function:
let findValues = x => {
if (R.has('values')(x)) {
let availableValues = R.prop('values')(x);
let selectedValues = R.pluck('a')(availableValues);
R.map(R.evolve({
availableValues: availableValues,
selectedValues: selectedValues
}))
}
}
The first, most obvious issue is that this function does not return anything. As mentioned above, Ramda does not mutate your data. So a function that does not return something is useless when supplied to Ramda's map. We can fix this by returning the result of the map(evolve) call and returning the original value when the if condition fails:
let findValues = x => {
if (R.has('values')(x)) {
let availableValues = R.prop('values')(x);
let selectedValues = R.pluck('a')(availableValues);
return R.map(R.evolve({
availableValues: availableValues,
selectedValues: selectedValues
}))
}
return x;
}
Next, the map call makes no sense. evolve already iterates the properties of the object. So we can remove that. But we also need to apply evolve to your input value:
let findValues = x => {
if (R.has('values')(x)){
let availableValues = R.prop('values')(x);
let selectedValues = R.pluck('a')(availableValues);
return R.evolve({
availableValues: availableValues,
selectedValues: selectedValues
}, x)
}
return x;
}
There's one more problem. Evolve expects an object containing functions that transform values. For instance,
evolve({
a: n => n * 10,
b: n => n + 5
})({a: 1, b: 2, c: 3}) //=> {a: 10, b: 7, c: 3}
Note that the values of the properties in the object supplied to evolve are functions. This code is supplying values. We can wrap those values with R.always, via availableValues: R.always(availableValues), but I think it simpler to use a lambda with a parameter of "_", which signifies that the parameter is unimportant: availableValues: _ => availableValues. You could also write availableValues: () => available values, but the underscore demonstrates the fact that the usual value is ignored.
Fixing this gets a function that works:
let findValues = x => {
if (R.has('values')(x)){
let availableValues = R.prop('values')(x);
let selectedValues = R.pluck('a')(availableValues);
return R.evolve({
availableValues: _ => availableValues,
selectedValues: _ => selectedValues
}, x)
}
return x;
}
let arrObj = [{myValues: []}, {availableValues: [], myValues: [], selectedValues: [], values: [{a: "x", b: "1"}, {a: "y", b: "2"}]}];
console.log(R.map(findValues, arrObj))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>
If you run this snippet here, you will also see an interesting point, that the resulting availableValues property is a reference to the original values one, not a separate copy of it. This helps show that Ramda is reusing what it can of your original data. You can also see this by noting that R.map(findValues, arrObj)[0] === arrObj[0] //=> true.
Other Approaches
I wrote my own versions of this, not starting from yours. Here is one working function:
const findValues = when(
has('values'),
x => evolve({
availableValues: _ => prop('values', x),
selectedValues: _ => pluck('a', prop('values', x))
}, x)
)
Note the use of when, which captures the notion of "when this predicate is true for your value, use the following transformation; otherwise use your original value." We pass the predicate has('values'), essentially the same as above. Our transformation is similar to yours, using evolve, but it skips the temporary variables, at the minor cost of repeating the prop call.
Something still bothers me about this version. Using those lambdas with "_" is really a misuse of evolve. I think this is cleaner:
const findValues = when(
has('values'),
x => merge(x, {
availableValues: prop('values', x),
selectedValues: pluck('a', prop('values', x))
})
)
Here we use merge, which is a pure function version of Object.assign, adding the parameters of the second object to those of the first, replacing when they conflict.
This is what I would choose.
Anther solution
Since I started writing this answer, Scott Christopher posted another one, essentially
const findValues = R.map(R.when(R.has('values'), R.applySpec({
myValues: R.prop('myValues'),
values: R.prop('values'),
availableValues: R.prop('values'),
selectedValues: R.o(R.pluck('a'), R.prop('values'))
})))
This is a great solution. It also is nicely point-free. If your input objects are fixed to those properties, then I would choose this over my merge version. If your actual objects contain many more properties, or if there are dynamic properties that should be included in the output only if they are in the input, then I would choose my merge solution. The point is that Scott's solution is cleaner for this specific structure, but mine scales better to more complex objects.
Step-by-step
This is not a terribly complex transformation, but it is non-trivial. One simple way to build something like this is to build it up in very minor stages. I often do this in the Ramda REPL so I can see the results as I go.
My process looked something like this:
Step 1
Make sure I properly transform only the correct values:
const findValues = when(
has('values'),
always('foo')
)
map(findValues, arrObj) //=> [{myValues: []}, "foo"]
Step 2
Make sure my transformer function is given the proper values.
const findValues = when(
has('values'),
keys
)
map(findValues, arrObj)
//=> [{myValues: []}, ["myValues", "values", "availableValues", "selectedValues"]]
Here identity would work just as well as keys. Anything that demonstrates that the right object is passed would be fine.
Step 3
Test that merge will work properly here.
const findValues = when(
has('values'),
x => merge(x, {foo: 'bar'})
)
map(findValues, arrObj)
//=> [
// {myValues: []},
// {
// myValues: [],
// values: [{a: "x", b: "1"}, {a: "y", b: "2"}],
// availableValues: [],
// selectedValues: [],
// foo: "bar"
// }
// ]
So I can merge two objects properly. Note that this keeps all the existing properties of my original object.
Step 4
Now, actually transform the first value I want:
const findValues = when(
has('values'),
x => merge(x, {
availableValues: prop('values', x)
})
)
map(findValues, arrObj)
//=> [
// {myValues: []},
// {
// myValues: [],
// values: [{a: "x", b: "1"}, {a: "y", b: "2"}],
// availableValues: [{a: "x", b: "1"}, {a: "y", b: "2"}],
// selectedValues: []
// }
// ]
Step 5
This does what I want. So now add the other property:
const findValues = when(
has('values'),
x => merge(x, {
availableValues: prop('values', x),
selectedValues: pluck('a', prop('values', x))
})
)
map(findValues, arrObj)
//=> [
// {myValues: []},
// {
// myValues: [],
// values: [{a: "x", b: "1"}, a: "y", b: "2"}],
// availableValues: [{a: "x", b" "1"}, {a: "y", b: "2"}],
// selectedValues: ["x", "y"]
// }
// ]
And this finally gives the result I want.
This is a quick process. I can run and test very quickly in the REPL, iterating my solution until I get something that does what I want.
R.evolve allows you to update the values of an object by passing them individually to their corresponding function in the supplied object, though it doesn't allow you to reference the values of other properties.
However, R.applySpec similarly takes an object of functions and returns a new function that will pass its arguments to each of the functions supplied in the object to create a new object. This will allow you to reference other properties of your surrounding object.
We can also make use of R.when to apply some transformation only when it satisfies a given predicate.
const input = [
{
"myValues": []
},
{
"myValues": [],
"values": [
{"a": "x", "b": "1"},
{"a": "y", "b": "2"}
],
"availableValues": [],
"selectedValues": []
}
]
const fn = R.map(R.when(R.has('values'), R.applySpec({
myValues: R.prop('myValues'),
values: R.prop('values'),
availableValues: R.prop('values'),
selectedValues: R.o(R.pluck('a'), R.prop('values'))
})))
const expected = [
{
"myValues": []
},
{
"myValues": [],
"values": [
{"a": "x", "b": "1"},
{"a": "y", "b": "2"}
],
"availableValues": [
{"a": "x", "b": "1"},
{"a": "y", "b": "2"}
],
"selectedValues": ["x", "y"]
}
]
console.log(R.equals(fn(input), expected))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
I am currently using sectionlist to create a list with a header, but I am struggling with dataSource.
Object {
"A": Array [
Object {
"fid": "2",
"name": "Manage",
"posts": "1",
"threads": "1",
"todayposts": "1",
},
],
"B": Array [
Object {
"fid": "36",
"name": "Anime",
"posts": "1",
"threads": "1",
"todayposts": "1",
},
Object {
"fid": "37",
"name": "Novel",
"posts": "2",
"threads": "2",
"todayposts": "2",
},
],
}
these are the data fetched from the server, and they are objects, so right now I have to transfer these data to a struct which can be accepted by section list, otherwise, I got an error message like,
TypeError:props.section.reduce is not a function.(In 'props.sections.reduce(function(v,section){
stickyHeaderIndices.push(v+offset);
return v + section.data.length +2;
},0)','props.sections.reduce' is undefined)
So what I tried is, using for loop to create a new array, but seems I failed.
Update:
<SectionList
sections={dataSource}
/>
So clearly I need a dataSource is an array with keys inside, not the one I have right now. So I need to find a way to trans the current object to array.
Can anyone help me with this?
So I figured out how to change it from object to an array with keys.
let SECTIONS = []
for (const key in dataSource) {
if (dataSource.hasOwnProperty(key)) {
SECTIONS.push({
data: dataSource[key],
key: key,
})
}
}
It solved my problem.
I am new to Ramda and I am trying to achieve the following:
I have an array of objects (i.e. messages).
I want to group the messages by counter party ID (either the sender or the recipient ID, whichever is not 1, see my groupBy lambda below).
I am going to obtain an object whose keys will be the counter party IDs and values an array of the messages exchanged with that counter party.
I then want to sort by date descending those arrays of messages.
And finally keep the most recent message and thus obtain an array containing the most recent message exchanged with each of the counter parties.
Because I have two counter parties above, I should have an array of two messages.
Here is what I have attempted:
const rawMessages = [
{
"sender": {
"id": 1,
"firstName": "JuliettP"
},
"recipient": {
"id": 2,
"firstName": "Julien"
},
"sendDate": "2017-01-28T19:21:15.863",
"messageRead": true,
"text": "ssssssss"
},
{
"sender": {
"id": 3,
"firstName": "Juliani"
},
"recipient": {
"id": 1,
"firstName": "JuliettP"
},
"sendDate": "2017-02-01T18:08:12.894",
"messageRead": true,
"text": "sss"
},
{
"sender": {
"id": 2,
"firstName": "Julien"
},
"recipient": {
"id": 1,
"firstName": "JuliettP"
},
"sendDate": "2017-02-07T22:19:51.649",
"messageRead": true,
"text": "I love redux!!"
},
{
"sender": {
"id": 1,
"firstName": "JuliettP"
},
"recipient": {
"id": 3,
"firstName": "Juliani"
},
"sendDate": "2017-03-13T20:57:52.253",
"messageRead": false,
"text": "hello Juliani"
},
{
"sender": {
"id": 1,
"firstName": "JuliettP"
},
"recipient": {
"id": 3,
"firstName": "Juliani"
},
"sendDate": "2017-03-13T20:56:52.253",
"messageRead": false,
"text": "hello Julianito"
}
];
const currentUserId = 1;
const groupBy = (m: Message) => m.sender.id !== currentUserId ? m.sender.id : m.recipient.id;
const byDate = R.descend(R.prop('sendDate'));
const sort = (value, key) => R.sort(byDate, value);
const composition = R.compose(R.map, R.head, sort, R.groupBy(groupBy));
const latestByCounterParty = composition(rawMessages);
console.log(latestByCounterParty);
Here is the corresponding codepen:
https://codepen.io/balteo/pen/JWOWRb
Can someone please help?
edit: Here is a link to the uncurried version: here. The behavior is identical without the currying. See my comment below with my question as to the necessity of currying.
While I think the solution from Scott Christopher is fine, there are two more steps that I might take with it myself.
Noting that one of the important rules about map is that
map(compose(f, g)) ≍ compose(map(f), map(g))
when we're already inside a composition pipeline, we can choose to unnest this step:
R.map(R.compose(R.head, R.sort(R.descend(R.prop('sendDate'))))),
and turn the overall solution into
const currentMessagesForId = R.curry((id, msgs) =>
R.compose(
R.values,
R.map(R.head),
R.map(R.sort(R.descend(R.prop('sendDate')))),
R.groupBy(m => m.sender.id !== id ? m.sender.id : m.recipient.id)
)(msgs)
)
Doing this, is, of course, a matter of taste. But I find it cleaner. The next step is also a matter of taste. I choose to use compose for things that can be listed on a single line, and hence make some obvious connection between the formats compose(f, g, h)(x) and f(g(h(x))). If it spans multiple lines, I prefer to use pipe, which behaves the same way, but runs it's functions from first to last. So I would change this a bit further to look like this:
const currentMessagesForId = R.curry((id, msgs) =>
R.pipe(
R.groupBy(m => m.sender.id !== id ? m.sender.id : m.recipient.id),
R.map(R.sort(R.descend(R.prop('sendDate')))),
R.map(R.head),
R.values
)(msgs)
)
I find this top-down reading easier than the bottom up needed with longer compose versions.
But, as I said, these are matters of taste.
You can see these examples on the Ramda REPL.
Your example was close to what you wanted, though you need just needed to move the composition of head and sort to the function argument given to map and then call values on the final result to convert the object to an array of values.
const currentMessagesForId = R.curry((id, msgs) =>
R.compose(
R.values,
R.map(R.compose(R.head, R.sort(R.descend(R.prop('sendDate'))))),
R.groupBy(m => m.sender.id !== id ? m.sender.id : m.recipient.id)
)(msgs))
currentMessagesForId(currentUserId, rawMessages)