Ramda - Accessing map's element with applySpec - ramda.js

I have the following code:
const commaString = "test,test1"
const getValueLabelList = compose(
map(
applySpec({
value: e => e,
label: e => e,
})
),
split(",")
)
getValueLabelList(commaString) -> [{ value: "test", label: "test" }, { value: "test1", label: "test1" }]
I have always wondered is there a Ramda function that can substitute the e => e ?
I would like to keep as declarative as possible.

Found the clone method which appears to do what I am looking for.
Not sure if this is the only way of doing this. So I won't accept this answer just yet.
Edit:
Scratch the answer above, identity appears to be the correct method. Although, I'll leave it, since it's potentially another way of achieving the same result.

While your own answer correctly identified identity as the likely Ramda function to use, I'm curious as to why you'd choose to use Ramda here. Ramda (disclaimer: I'm one of Ramda's founders) is a library, a tool meant to make a certain coding style easier in Javascript. It is not a framework. I'd suggest that you use it only when it simplifies your code. And this vanilla version seems simpler:
const getValueLabelList = (str) =>
str .split (',') .map (s => ({value: s, label: s}))
const commaString = "test,test1"
console .log (
getValueLabelList (commaString)
)
(When I've made this point before, some people have said that they were performing a learning exercise for Ramda. If that applies to you, never mind the above.)

Related

Migrate from Immutable to Immer, missing object.filter()

I'm investigating how much work it would be to migrate my React-Native app from Immutable.js to Immer. A lot of problems arise, but I'll address them one by one here at SO.
Problem #1: object.filter()
An Immutable.Map can be iterated just like an array with map and filter. Plain JS objects cannot. Here is one way to solve this, but I wonder if there are better ways? Is this performant?
The result fom this will be memoized with ReSelect.
All my items has an "id" property, which will be the same as the key.
const myCollection = {
1: {id:1, name: 'abc'}
2: {id:2, name: 'def'}
}
const filteredCollection = Object.fromEntries(
Object.entry(myCollection)
.filter(item => item.someValue === 'filter-criteria')
.map(item => [item.id, item]
)

cy.clear() not clearing input field properly - Cypress

When I perform
cy.get('#fsp-name').clear().type('random text');
If the text already has value lets say 'assd asd adsdsd' and I perform above command I get something similar to 'random textassd'
I also tried using
cy.get('#fsp-name').clear().should('have.value', '').type('random text');
It works some time and in other times it complains it does not equal to ' '.
And I am trying to do this in a each loop like below
const data = [
{selector:'#name', newValue: 'John'},
{selector:'#phone', newValue: '1234567'}
];
cy.wrap(data).each(field => {
cy.get(field.selector).clear().should('have.value', '').type(field.newValue);
cy.contains('Save').click();
cy.visit('/abc/sdd');
cy.get(field.selector).invoke('val').should('equal', field.newValue);
});
Tried the solutions provided above, but all did not help.
I've ended up using this:
cy.get('#my-input-element').focus().clear();
If that doesn't work, the not so happy workaround is:
cy.get('#my-input-element').invoke('val', '');
When .type somehow did not finish the given string (rare cases):
cy.get('#my-input-element').invoke('val', 'Some text here');
I had a similar problem and It was related to focused and click related. I can suggest trying the following two option. I DON'T know it is right or wrong.
cy.get('#fsp-name').click().clear().type('random text');
OR
cy.get('#fsp-name').click().focused().clear().type('random text');
I was talking to the developer and according to him we are using MaterialUI and have some default component using focused and click event differently. After having both options resolved my problem
.clear() is an alias of .type('{selectall}{backspace}') however depending upon the input field set up this would not work in all cases.
I solved this by using .type('{selectall}{backspace}{selectall}{backspace}') instead of the .clear()
I'm using Cypress version 3.8.3 and I noticed that I have to invoke clear() sometimes two times in a row:
cy.get('#fsp-name').clear();
cy.get('#fsp-name').clear();
Seems like the cypress test runner is getting ahead of app initialization and some helpful article links below
https://www.cypress.io/blog/2018/02/05/when-can-the-test-start/
https://www.cypress.io/blog/2019/01/22/when-can-the-test-click/
As of now adding wait before clearing makes the test pass. Let me know if anyone has better solutions
I've had the same problem using Mui React with Cypress and when I called clear an ";" was added.
I've applied the same #Steven Vachon solution calling clear() function of cypress first.
Here my solution:
const clearInputElement = (input) => {
const input2Search = input;
cy.get(input2Search).clear();
cy.get(input2Search).then(($elm) => {
const event = new Event(input2Search, { bubbles: true, cancelable: true });
const input = $elm.get(0); // `as HTMLInputElement` for TypeScript
input.value = "";
input.dispatchEvent(event);
});
};
I ended up having to do clear manually via the DOM:
cy.get('input').then($elm => {
const event = new Event('input', { bubbles: true, cancelable: true });
const input = $elm.get(0); // `as HTMLInputElement` for TypeScript
input.value = '';
input.dispatchEvent(event);
});
I, too, faced a similar issue while using with react-ace editor. I wind up with
function typeContentOnSelectingExistingContent(elementId, content) {
return cy.get(`#${elementId}`).type(`{selectAll}{selectAll}${content}`)
}
Try this, it worked for me:
cy.get('#fsp-name').clear({ force: true }).then(() => {
cy.wait(3000)
cy.get('#fsp-name').invoke('val', '').type(`${valueToBeTyped}{enter}`)
})
Official docs states that:
It is unsafe to chain further commands that rely on the subject after .clear().
That's probably why the code in the original question didn't work, it was chaining clear and type commands:
cy.get('#fsp-name').clear().type('random text');
So, a simple alternative would be something like:
cy.get('#fsp-name').clear()
cy.get('#fsp-name').type('some text')
More about the clear command:
https://docs.cypress.io/api/commands/clear

ramda findIndex with gaps

I have the following code where I want to fill in the id, so I'm thinking to write something like this:
const data = [
{ id: 'some-id' },
{ id: 'some-other-id' },
{ id: 'third-id' },
];
const tabIndex = R.findIndex(R.propEq('id', R.__))(data);
So I can use it like this tabIndex('third-id'), but this is not a function.
What do I miss or confuse with this?
The following works
const tabIndex = (id) => R.findIndex(R.propEq('id', id))(data);
But I thought, that is the point of R.__ gaps function.
I think that by far the simplest way to do this is
const matchId = (id, data) => R.findIndex(R.propEq('id', id), data);
matchId('third-id', data); //=> 2
If you really want to make this points-free, Ramda offers several functions to help, such as useWith and converge (for which one can often substitute lift.) This one would take useWith:
const matchId = R.useWith(findIndex, [R.propEq('id'), R.identity]);
matchId('third-id', data); //=> 3
But I find the first version much more readable. You can find both on the Ramda REPL.
Do pay attention to the side note from Emissary. The R.__ placeholder is essentially used to show gaps between the arguments you supply; as a final argument it doesn't do anything.
I'm still trying to master this dark art myself but I think the issue is that R.findIndex expects a predicate (a function / assertion) as an argument and does not differentiate between predicates and regular curried functions as input.
To resolve this a new function can be composed (evaluated right to left):
const data = [
{ id: 'some-id' },
{ id: 'some-other-id' },
{ id: 'third-id' }
];
const tabIndex = R.compose(R.findIndex(R.__, data), R.propEq('id'));
console.log(tabIndex('third-id')); // 2
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.24.1/ramda.min.js"></script>
Side Note: the R.__ placeholder is inferred automatically for missing right-most arguments - e.g. R.propEq('id') and R.propEq('id', R.__) are equivalent.

Find matching elements in array and do something for each match

I'm a little overwhelmed by all the functions available to me in Lodash, so I hope someone can point me to the one I'm sure exists which will do the following for me. I want to be able to pass an array and a search condition, and have it loop through all the matched items, allowing me to run a function for each. What I have at the moment is something akin to this:
_.each(myArray, (item) => {
if (item.field=="whatever") {
console.log("Matched by "+item.name);
}
});
This works fine, of course. It's just that I'm sure Lodash has a way for me to move the item.field=="whatever" into the function arguments somehow, and I'd prefer to go with the more idiomatic Lodash way if I can.
It's just that I'm sure Lodash has a way for me to move the item.field == "whatever" into the function arguments somehow
If you want to find all the matching items in an array based on the arguments you pass in, then you could use the _.filter method, which can use the _.matches shorthand internally:
_.filter(myArray, { field: 'whatever' });
However, you would still need to loop over the items if you want to do something for each match:
_.each(_.filter(myArray, { field: 'whatever' }), item => {
console.log("Matched by " + item.name);
});
Alternatively, if you want a different way of writing this, you can wrap the filtered items with the lodash object wrapper, _(), which essentially enables chaining, thereby allowing you to chain the _.each() method:
_(_.filter(myArray, { field: 'whatever' })).each(item => {
console.log("Matched by " + item.name);
});
Or a more readable version:
var matchedItems = _.filter(myArray, { field: 'whatever' });
_(matchedItems).each(item => {
console.log("Matched by " + item.name);
});
Personally, I would probably just keep what you originally wrote since it's short, readable and easy to maintain.

React Native: Can't use this.setState() to set a variable inside a 2D array, but this.state.x= works

I have a 2D array of objects with key/value pairs as a state variable, and I'm trying to use the recommended way of setting/changing state variables, which is to use this.setState({x:y}) instead of directly setting it using this.state.x = y and then forceUpdate(). However, when I try to do that, it gives me an "unexpected token" error.
I basically want to flip a variable from one state to the other, so I'm usng a ternary operator. This code works
toggleBookmark(category, index) {
this.state.menuItems[category][index].bmIcon = (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o";
}
This code, which I'd expect to do the same thing, gives an error
toggleBookmark(category, index) {
this.setState({menuItems[category][index].bmIcon: (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o"});
}
I thought it might be the ternary operator, so I put the value into a variable and tried setting the state variable with that, but it still gives the same error.
toggleBookmark(category, index) {
var iconText = (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o";
this.setState({menuItems[category][index].bmIcon: iconText});
}
Am I doing something wrong? Is what I want to do possible with setState()?
In Javascript, you cannot use an expression as a key for an object when creating that object inline.
The problem here is that you have done {menuItems[category][index].bmIcon: iconText} which will throw a syntax error.
If you want a quick way to solve this, you may create the object first, then assign the value to that key like this:
var state = {};
state[menuItems[category][index].bmIcon] = iconText;
this.setState(state);
It's worth noting however that ES6 Provides a sugar for doing this, and there is another answer here that might provide more insight
How do I create a dynamic key to be added to a JavaScript object variable
Update:
I now see what you meant, I had previously assumed that menuItems already defined, but what you want to do is change the value of a key inside a nested object that is in this.state
This is something that React is not really built to do, you should keep your state relatively simple, and make separate React components for each menu item, then have them manage their own state. I would strongly recommend this approach because it will keep your code clean and robust. Don't be afraid to make more components!
However if you do want to keep all this nested state in one component (not advised), then you should first make a copy of the object you want to setState on.
var newMenuItems = _.clone(this.state.menuItems);
var iconText = (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o";
newMenuItems[category][index].bmIcon = iconText;
this.setState({ menuItems: newMenuItems });
OR
var iconText = (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o";
this.state.menuItems[category][index].bmIcon = iconText;
this.forceUpdate();
(First method preferred, but it requires you have something like underscore or lodash installed )
I have the data chat:
chat: {
id: 'ss3k5e6j1-6shhd6-sdasd3d3-23d5-gh67',
agentName: 'egaliciar',
agentAvatar: 'http://i.imgur.com/DY6gND0.png',
messages: [
{
id: 1,
lines: [
'Me pueden ayudar?',
'Tengo problemas con mis boletos',
'Hola buen dia...',
],
time: '17:20',
},
{
id: 2,
lines: ['¿Me podria regalar su nombres', 'Con gusto...'],
time: '17:22',
date: '23/ene/2012',
},
],
},
};
and when i do
const oldLines =Object.assign({}, this.state.chat);
oldLines.messages[0].lines.push('newValue');
My state Changed..... without this.setState({});
I Made a Clone;
var clone = JSON.parse(JSON.stringify(this.state.chat));
clone.messages[0].lines.push('new Value');
and the State maintain their state;
thus, the complete solution is for me:
var clone = JSON.parse(JSON.stringify(this.state.chat));
clone.messages[0].lines.push(questionAreaMessage); //the state maintains
this.setState({chat:clone}); //here the State change!!!!