Unable to catch arguments in "R.applySpec" : Ramda.js - ramda.js

I am basically trying to achieve the code below within the R.applySpec.
const fn = ({target, count}) => R.unnest (R.zipWith (R.repeat) (target, count))
const Data = {
target : ["a", "b", "c"],
count : [1, 2, 3],
}
const data1= {
result : fn (Data)
}
console.log ( 'data1:', data1.result);// ["a","b","b","c","c","c"]
What I cannot figure out is that arguments in the fn seems to be uncaught within the R.applySpec
const data2_applySpec = R.applySpec({
result : R.lift ( R.zipWith ( fn )) ( R.prop ('target'), R.prop ('count'))
})
const data2 = data2_applySpec(Data)
console.log ('data2:', data2);//ERROR
How can I alter the fn to make it work?
I use Ramda.js.
Thanks.
REPL

You can can an array of arrays ([target, count]) using R.props, apply the array of array to R.zipWith(repeat), and then flatten the results with R.unnest:
const { applySpec, pipe, props, apply, zipWith, repeat, unnest } = R
const Data = {
target : ["a", "b", "c"],
count : [1, 2, 3],
}
const data2_applySpec = applySpec({
result: pipe(props(['target', 'count']), apply(zipWith(repeat)), unnest)
})
const result = data2_applySpec(Data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>

I think you're making this harder than it needs to be.
You already have the function you want to use inside applySpec stored as fn.
So you can just write:
const fn2 = applySpec ({
result: fn
})
Or, if your only use of fn is inside this applySpec call, then just inline it:
const fn3 = applySpec ({
result: ({target, count}) => unnest (zipWith (repeat) (target, count))
})
And if you have a fetish for point-free code, you can use the technique discussed in your earlier post:
const fn4 = applySpec ({
result: compose (unnest, apply (zipWith (repeat)), props (['target', 'count']))
})
(or the similar version from Ori Drori.)
All of these are shown in this snippet.
const fn1 = ({target, count}) => unnest (zipWith (repeat) (target, count))
const fn2 = applySpec ({
result: fn1
})
const fn3 = applySpec ({
result: ({target, count}) => unnest (zipWith (repeat) (target, count))
})
const fn4 = applySpec ({
result: compose (unnest, apply (zipWith (repeat)), props (['target', 'count']))
})
const data = {target : ["a", "b", "c"], count : [1, 2, 3]}
console .log (fn1 (data))
console .log (fn2 (data))
console .log (fn3 (data))
console .log (fn4 (data))
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
<script> const {unnest, zipWith, repeat, applySpec, compose, apply, props} = R </script>

Related

Pick fields from an object and return one field as an array

Using Ramda, I am trying to achieve something like this:
let data = {
'accountNumber' : '12345',
'holderName' : 'XYZ',
'id' : '12XX',
'type' : 'savings',
...rest
}
let newObj = R.pick(['accountNumber', 'id', 'type']) (data);
output -> newObj = {
'accountNumber' : '12345',
'id' : '12XX',
'type' : 'savings'
}
I want result like this:
newObj = {
'accountNumbers' : ['12345'],
'id' : '12XX',
'type' : 'savings'
}
I want accountNumber field to be put inside an array of accountNumbers.
I understand pick won't work in this case but I am not able to achieve it through Ramda. It can be done easily using JavaScript but is it possible to achieve via Ramda?
You can pick the properties, and create a new object with changed keys using R.applySpec():
const { applySpec, pipe, of, prop } = R
const fn = applySpec({
accountNumbers: pipe(prop('accountNumber'), of),
holderName: prop('holderName'),
id: prop('id')
})
const data = {
'accountNumber': '12345',
'holderName': 'XYZ',
'id': '12XX',
'type': 'savings',
}
const result = fn(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
Another option is to add accountNumbers using R.applySpec (based on this answer), and then pick the properties, including the updated one from the object:
const { pipe, applySpec, chain, mergeLeft, prop, of, pick } = R
const updateObject = pipe(applySpec, chain(mergeLeft))
const addAccountNumbers = updateObject({
accountNumbers: pipe(prop('accountNumber'), of)
})
const fn = pipe(addAccountNumbers, pick(['accountNumbers', 'holderName', 'id']))
const data = {
'accountNumber': '12345',
'holderName': 'XYZ',
'id': '12XX',
'type': 'savings',
}
const result = fn(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

lodash - is there a method to filter by array of single property?

const buttons = [
{ id: 1, text: 'First' },
{ id: 2, text: 'Second' },
{ id: 3, text: 'Third' }
]
const activeButtonIds = [1, 3]
Using lodash, I want to filter out all buttons with ids not included within activeButtonIds = [1, 3].
The obvious way of doing that is:
_.filter(buttons, ({ id }) => _.includes(activeButtonIds, id))
But I was wondering, is there a simpler way of achieving the same thing? A built-in function for this within lodash?
You can use _.intersectionWith() to find items that are included in both arrays using a comperator function:
const buttons = [{"id":1,"text":"First"},{"id":2,"text":"Second"},{"id":3,"text":"Third"}]
const activeButtonIds = [1, 3]
const result = _.intersectionWith(buttons, activeButtonIds, (button, activeId) =>
button.id === activeId
)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Ramda - get ids from nested array of multiple objects

I am trying to get outgoingNodes IDs which are stored in array which is inside objects like in example below but I have no idea where to start...:
const nodes = {
"818": {
"id": "818",
"index": 1,
"outgoingNodes": [
"819"
],
},
"819": {
"id": "819",
"outgoingNodes": [
"820",
"821"
],
}
}
I would like to get an array of IDs as a result. Any help will be appreciated.
Get the values (sub objects), pluck the outgoingNodes arrays, and flatten to a single array:
const { pipe, values, pluck, flatten } = R
const fn = pipe(
values,
pluck('outgoingNodes'),
flatten
)
const nodes = {"818":{"id":"818","index":1,"outgoingNodes":["819"]},"819":{"id":"819","outgoingNodes":["820","821"]}}
const result = fn(nodes)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>
Another option is to combine getting the outgoingNodes arrays, and flattening to a single array using R.chain with R.prop:
const { pipe, values, chain, prop } = R
const fn = pipe(
values,
chain(prop('outgoingNodes')),
)
const nodes = {"818":{"id":"818","index":1,"outgoingNodes":["819"]},"819":{"id":"819","outgoingNodes":["820","821"]}}
const result = fn(nodes)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>

Ramda.js - How to make R.merge point free - 2 functions with same dataset

Given the below code snippet that uses Ramda.js, are there multiple ways of making parseDetails point free? If so, is there an "ideal" way of doing it?
const someData = {
products: [{
stockId: 123,
name: "chocolate",
translatedTags: {
wasPrice: "Was 10"
}
}]
}
const getProductData = R.pipe(
R.pathOr([], ['products', 0]),
R.pick([
'stockId',
'name']
)
);
const getProductTags = R.pipe(
R.pathOr([], ['products', 0, 'translatedTags'])
);
const parseDetails = (data) => R.merge(
getProductData(data),
getProductTags(data)
);
parseDetails(someData);
There are several ways I can think of, but by far the best is to use lift. The way I think of using lift is that it takes a function which works on values and returns an equivalent function which works on containers of those values.
And functions that return those values can be thought of as containers. So we can do it like this:
const getProductData = pipe (
pathOr ([], ['products', 0]),
pick ([
'stockId',
'name']
)
)
// `pipe` not necessary here
const getProductTags = pathOr ([], ['products', 0, 'translatedTags'])
const parseDetails = lift (merge) (getProductData, getProductTags)
const someData = {products: [{stockId: 123, name: "chocolate", translatedTags: {wasPrice: "Was 10"}}]}
console .log (parseDetails (someData))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
<script> const {pipe, pathOr, pick, lift, merge} = R </script>
And if you have no other need for getProductData or getProductTag, you can inline them in the function:
const parseDetails = lift (merge) (
pipe (pathOr ([], ['products', 0]), pick (['stockId', 'name'])),
pathOr ([], ['products', 0, 'translatedTags'])
)

Ramda, concat with propOr/pathOr

const obj = {
psets: [...],
type: {
psets: [...]
}
}
Want to concat the psets props. Both of them may not exist.
R.concat(R.pathOr([], ['type','pSets']), R.propOr([], 'pSets'));
**
Uncaught TypeError: function n(r){return 0===arguments.length||w(r)?n:t.apply(this,arguments)} does not have a method named "concat"
What am I doing wrong?
R.concat expects arrays or strings, and not functions. You can use R.converge to prepare the arrays for concat.
Note: R.__ is used as a placeholder for incoming arguments that you to assign to a different position than the last parameter.
const obj = {
pSets: [1, 2],
type: {
pSets: [3, 4]
}
}
const fn = R.converge(R.concat, [
R.pathOr([], ['type','pSets']),
R.propOr([], 'pSets')]
)
const result = fn(obj)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
Another option that will make the code DRYer is to use R.chain to iterate the paths, get the the values from the object, and concat them:
const obj = {
pSets: [1, 2],
type: {
pSets: [3, 4]
}
}
const fn = R.curry((paths, obj) => R.chain(R.pathOr([], R.__, obj), paths))
const result = fn([['pSets'], ['type','pSets']], obj)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>