Replace value of key using ramda.js - ramda.js

I have the following array of objects:
const originalArray = [
{name: 'name1', value: 10},
{name: 'name2', value: 20}
]
And the following object
names = {
name1: 'generic_name_1',
name2: 'generic_name_2'
}
I would like the first array to be transformed like this:
[
{name: 'generic_name_1', value: 10},
{name: 'generic_name_2', value: 20}
]
What I have tried so far:
const replaceName = (names, obj) => {
if(obj['name'] in names){
obj['name'] = names[obj['name']];
}
return obj;
}
const modifiedArray = R.map(replaceName(names), originalArray)
Is there a more ramda-ish way to do this?

Using native JS inside Ramda functions is not unramdaish. The only problem in your code is that you mutate the original object - obj['name'] = names[obj['name']];.
I would use R.when to check if the name exists in the names object, and if it does evolve the object to the new name. If it doesn't the original object would be returned.
const { flip, has, prop, map, when, pipe, evolve } = R
const hasProp = flip(has)
const getProp = flip(prop)
const fn = names => map(when(
pipe(prop('name'), hasProp(names)),
evolve({
name: getProp(names)
})
))
const originalArray = [{"name":"name1","value":10},{"name":"name2","value":20},{"name":"name3","value":30}]
const names = {"name1":"generic_name_1","name2":"generic_name_2"}
const result = fn(names)(originalArray)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>

I wouldn't use any Ramda functions for this. I would simply avoid mutating the original, perhaps with code like this:
const transform = (names) => (arr) => arr .map (
({name, ... rest}) => ({name: names [name] || name, ... rest})
)
const originalArray = [{name: 'name1', value: 10},{name: 'name2', value: 20}]
const names = {name1: 'generic_name_1',name2: 'generic_name_2'}
console .log (
transform (names) (originalArray)
)

Related

Vue test blob object issue

I am trying to convert string to blob but I keep getting {} instead
const workingCSV = "Hey, hello, world,"
const localVue = createLocalVue()
const wrapper = shallowMount(ModalAddCollectionCSV, {
localVue,
propsData: {
visible: true,
},
})
const fileDict = [{ file: new Blob([workingCSV]) }]
fileDict is always [{file:{}}], I need it in that format because loadcsv reads the first file passed to it, but here the first file is always empty
You should create your blob in this way:
const csvData = [
['UPC', 'SKU', 'Description', 'Cost', 'Quantity', 'Bin location', 'Category', 'Primary supplier', 'Value'].join(';') // header row with names of the columns
].concat(originalItemsArray.map(item => [ // only extract the columns we need
item.upc,
item.sku,
item.product_name,
+item.cost || 0,
+item.level || 0,
item.bin_location,
item.category_name,
item.supplier_name,
(+item.level || 0) * (+item.cost || 0),
].map(val => JSON.stringify(val)).join(';'))); // properly quote the value of each column
const myBlob = new Blob([csvData.join('\r\n')], { type: 'text/csv;charset=cp1250;' });

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>

Ramda: Split a list to segments

If we have a list such as:
[
{
type: 'a',
},
{
type: 'a',
},
{
type: 'b',
},
{
type: 'a',
}
]
... and we want to segment it to create a list, such that the new list is made up of each segment of the initial list, here split by type, looking like:
[
[
{
type: 'a',
},
{
type: 'a',
},
],
[
{
type: 'b',
},
],
[
{
type: 'a',
}
]
]
I'd like to create a general purpose 'segmenting' function, which takes a function to compare two items, and determine whether or not a new segment is required. Here, the 'segmenter' for that function simply compares type.
I can write that in vanilla javascript, but is there a good way to do this with Ramda?
const data = [
{
type: 'a',
},
{
type: 'a',
},
{
type: 'b',
},
{
type: 'a',
}
];
const segmentBy = segmenter => items => {
const segmentReducer = (prev = [], curr) => {
let lastSegment = [];
let lastItem = null;
try {
lastSegment = prev[prev.length - 1];
lastItem = lastSegment[lastSegment.length - 1];
} catch (e) {
return [...prev, [curr]];
}
const requiresNewSegment = segmenter(lastItem, curr);
if (requiresNewSegment) {
return [...prev, [curr]];
}
return [...prev.slice(0, prev.length - 1), [...lastSegment, curr]];
};
return items.reduce(segmentReducer, []);
};
const segmentByType = segmentBy((a, b) => a.type !== b.type);
const segments = segmentByType(data);
console.dir(segments);
With Ramda you can use R.groupWith:
Takes a list and returns a list of lists where each sublist's elements
are all satisfied pairwise comparison according to the provided
function. Only adjacent elements are passed to the comparison
function.
const data = [{"type":"a"},{"type":"a"},{"type":"b"},{"type":"a"}];
const segmentByType = R.groupWith(R.eqBy(R.prop('type')));
const segments = segmentByType(data);
console.dir(segments);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
In vanilla, the main problem is when to add a new subarray to the accumulator. You need to add another subarray when it's the 1st item, or if segmenter returns true.
const data = [{"type":"a"},{"type":"a"},{"type":"b"},{"type":"a"}];
const segmentBy = segmenter => items =>
items.reduce((r, item, i, arr) => {
if(i === 0 || segmenter(item, arr[i - 1])) r.push([]);
r[r.length - 1].push(item);
return r;
}, []);
const segmentByType = segmentBy((a, b) => a.type !== b.type);
const segments = segmentByType(data);
console.dir(segments);

Ramda js maximum elements

I wonder how will be the best way to get max elements from array.
For example I have regions with temperaturs:
let regions = [{name: 'alabama', temp: 20}, {name: 'newyork', temp: 30}...];
It can be done with one line but I want to be performant.
I want to iterate over the array only once.
If more than 1 region has the same max temperature i want to get them all
Do you know a way to make it with more compact code than procedure code with temporary variables and so on.
If it can be done in "functional programming" way it will be very good.
This is sample procedure code:
regions = [{name:'asd', temp: 13},{name: 'fdg', temp: 30}, {name: 'asdsd', temp: 30}]
maxes = []
max = 0
for (let reg of regions) {
if (reg.temp > max) {
maxes = [reg];
max = reg.temp
} else if (reg.temp == max) {
maxes.push(reg)
} else {
maxes =[]
}
}
Another Ramda approach:
const {reduce, append} = R
const regions = [{name:'asd', temp: 13},{name: 'fdg', temp: 30}, {name: 'asdsd', temp: 30}]
const maxTemps = reduce(
(tops, curr) => curr.temp > tops[0].temp ? [curr] : curr.temp === tops[0].temp ? append(curr, tops) : tops,
[{temp: -Infinity}]
)
console.log(maxTemps(regions))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>
This version only iterates the list once. But it's a bit ugly.
I would usually prefer the version from Ori Drori unless testing shows that the performance is a problem in my application. Even with the fix from my comment, I think that code is easier to understand than this one. (That wouldn't be true if there were only two cases. (< versus >= for instance.) But when there are three, this gets hard to read, however we might format it.
But if performance is really a major issue, then your original code is probably faster than this one too.
Use R.pipe to
Group the objects by temp's value,
Convert the object of groups to an array of pairs
Reduce the pairs to the one with the max key (the temp)
return the value from the pair
const { pipe, groupBy, prop, toPairs, reduce, maxBy, head, last } = R;
const regions = [
{name: 'california', temp: 30},
{name: 'alabama', temp: 20},
{name: 'newyork', temp: 30}
];
const result = pipe(
groupBy(prop('temp')),
toPairs,
reduce(maxBy(pipe(head, Number)), [-Infinity]),
last
)(regions);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>
A different approach to this (albeit a little more verbose) is to create some helpers to generically take care of folding over a list of things to extract the list of maximums.
We can do this by defining a Semigroup wrapper class (could also be a plain function instead of a class).
const MaxManyBy = fn => class MaxMany {
constructor(values) {
this.values = values
}
concat(other) {
const otherValue = fn(other.values[0]),
thisValue = fn(this.values[0])
return otherValue > thisValue ? other
: otherValue < thisValue ? this
: new MaxMany(this.values.concat(other.values))
}
static of(x) {
return new MaxMany([x])
}
}
The main purpose of this class is to be able to combine two lists by comparing the values contained within, with the invariant that each list contains the same comparable values.
We now can introduce a new helper function which applies some function to each value of a list and then combines them all using concat.
const foldMap = (fn, [x, ...xs]) =>
xs.reduce((acc, next) => acc.concat(fn(next)), fn(x))
With these helpers, we can now create a function that pulls the maximum temperatures from your example.
const maxTemps = xs =>
foldMap(MaxManyBy(({temp}) => temp).of, xs).values
maxTemps([
{name: 'california', temp: 30},
{name: 'alabama', temp: 20},
{name: 'newyork', temp: 30}
])
//=> [{"name": "california", "temp": 30}, {"name": "newyork", "temp": 30}]
There is an assumption here that the list being passed to foldMap is non-empty. If there's a chance that you'll encounter an empty list then you will need to modify accordingly to return a default value of some kind (or wrap it in a Maybe type if no sane default exists).
See the complete snippet below.
const MaxManyBy = fn => class MaxMany {
constructor(values) {
this.values = values
}
concat(other) {
const otherValue = fn(other.values[0]),
thisValue = fn(this.values[0])
return otherValue > thisValue ? other
: otherValue < thisValue ? this
: new MaxMany(this.values.concat(other.values))
}
static of(x) {
return new MaxMany([x])
}
}
const foldMap = (fn, [x, ...xs]) =>
xs.reduce((acc, next) => acc.concat(fn(next)), fn(x))
const maxTemps = xs =>
foldMap(MaxManyBy(({temp}) => temp).of, xs).values
const regions = [
{name: 'california', temp: 30},
{name: 'alabama', temp: 20},
{name: 'newyork', temp: 30}
]
console.log(maxTemps(regions))

Transform objects pointfree style with Ramda

Given the function below, how do I convert it to point-free style? Would be nice to use Ramda's prop and path and skip the data argument, but I just can't figure out the proper syntax.
const mapToOtherFormat = (data) => (
{
'Name': data.Name
'Email': data.User.Email,
'Foo': data.Foo[0].Bar
});
One option would be to make use of R.applySpec, which creates a new function that builds objects by applying the functions at each key of the supplied "spec" against the given arguments of the resulting function.
const mapToOtherFormat = R.applySpec({
Name: R.prop('Name'),
Email: R.path(['User', 'Email']),
Foo: R.path(['Foo', 0, 'Bar'])
})
const result = mapToOtherFormat({
Name: 'Bob',
User: { Email: 'bob#example.com' },
Foo: [{ Bar: 'moo' }, { Bar: 'baa' }]
})
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.22.1/ramda.min.js"></script>
Here's my attempt:
const mapToOtherFormat = R.converge(
(...list) => R.pipe(...list)({}),
[
R.pipe(R.view(R.lensProp('Name')), R.set(R.lensProp('Name'))),
R.pipe(R.view(R.compose(R.lensProp('User'), R.lensProp('Email'))), R.set(R.lensProp('Email'))),
R.pipe(R.view(R.compose(R.lensProp('Foo'), R.lensIndex(0), R.lensProp('Bar'))), R.set(R.lensProp('Foo')))
]
)
const obj = {Name: 'name', User: {Email: 'email'}, Foo: [{Bar: 2}]}
mapToOtherFormat(obj)
Ramda console
[Edit]
We can make it completely point-free:
const mapToOtherFormat = R.converge(
R.pipe(R.pipe, R.flip(R.call)({})),
[
R.pipe(R.view(R.lensProp('Name')), R.set(R.lensProp('Name'))),
R.pipe(R.view(R.compose(R.lensProp('User'), R.lensProp('Email'))), R.set(R.lensProp('Email'))),
R.pipe(R.view(R.compose(R.lensProp('Foo'), R.lensIndex(0), R.lensProp('Bar'))), R.set(R.lensProp('Foo')))
]
)
Ramda console