Ramda: filtering an array against an array of items - ramda.js

I have a working function to filter the following array:
const arrayOne = [
{
node: {
caseStudyFields: {
filterTags: [
"Temperature control"
]
}
}
},
{
node: {
caseStudyFields: {
filterTags: null
}
}
},
{
node: {
caseStudyFields: {
filterTags: [
"Specialist manufacturing",
"Pharmaceuticals"
]
}
}
},
]
const arrayTwo = [
'Pharmaceuticals',
'Specialist manufacturing',
'Temperature control'
]
const fn = n => n.node.caseStudyFields.filterTags &&
n.node.caseStudyFields.filterTags.some(r => arrayTwo.includes(r))
return arrayOne.filter(fn)
This code works fine, but I wanted to convert it to Ramda (for fun). I've got so far in finding the path but I've become confused with the some and includes (any in Ramda?)
const filter = R.filter(
R.pipe(
R.path(['node', 'caseStudyFields', 'filterTags']),
)
);
return filter(arrayOne)

Use R.pathOr to get the value at the path, and return and empty array if it's null. This will prevent filter from erroring when encountering the null.
Use R.any (Ramda's equivalent to Array.some()) with R.includes, curried with the array of tags, to find matching items:
const { curry, filter, pipe, pathOr, any, includes, __ } = R
const filterByTags = curry((tags, arr) =>
filter(pipe(
pathOr([], ['node', 'caseStudyFields', 'filterTags']),
any(includes(__, tags))
))
(arr))
const arrayOne = [{"node":{"caseStudyFields":{"filterTags":["Temperature control"]}}},{"node":{"caseStudyFields":{"filterTags":null}}},{"node":{"caseStudyFields":{"filterTags":["Specialist manufacturing","Pharmaceuticals"]}}}]
const arrayTwo = ["Pharmaceuticals","Specialist manufacturing","Temperature control"]
const result = filterByTags(arrayTwo, arrayOne)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js" integrity="sha512-3sdB9mAxNh2MIo6YkY05uY1qjkywAlDfCf5u1cSotv6k9CZUSyHVf4BJSpTYgla+YHLaHG8LUpqV7MHctlYzlw==" crossorigin="anonymous"></script>

Related

Detox get length of element

Hi i'm using detox and i would like to know how can I get the number of matches to
one element(length).
For example "card" match three times, how can I get the three.
const z = await element(by.id("card"))
https://github.com/wix/Detox/blob/master/docs/APIRef.Expect.md
https://github.com/wix/Detox/blob/master/docs/APIRef.Matchers.md
They don't support it in the API /:
z output:
Element {
_invocationManager: InvocationManager {
executionHandler: Client {
isConnected: true,
configuration: [Object],
ws: [AsyncWebSocket],
slowInvocationStatusHandler: null,
slowInvocationTimeout: undefined,
successfulTestRun: true,
pandingAppCrash: undefined
}
},
matcher: Matcher { predicate: { type: 'id', value: 'card' } }
}
A workaround could be
async function getMatchesLength(elID) {
let index = 0;
try {
while (true) {
await expect(element(by.id(elID)).atIndex(index)).toExist();
index++;
}
} catch (error) {
console.log('find ', index, 'matches');
}
return index;
}
then you can use
const length = await getMatchesLength('card');
jestExpect(length).toBe(3);
Here is my solution in typescript:
async function elementCount(matcher: Detox.NativeMatcher) {
const attributes = await element(matcher).getAttributes();
// If the query matches multiple elements, the attributes of all matched elements is returned as an array of objects under the elements key.
https://wix.github.io/Detox/docs/api/actions-on-element/#getattributes
if ("elements" in attributes) {
return attributes.elements.length;
} else {
return 1;
}
}
Then you can use it like this:
const jestExpect = require("expect");
jestExpect(await elementCount(by.id("some-id"))).toBe(2);

Using Ramda to accumulate sum of values in objects

I have an object like this:
obj = {
'key1': {'prop1': 123,'prop2':345},
'key2': {'prop1': 673,'prop3':642}
}
I would like to have the following result:
result = {'prop1': 796, 'prop2':345, 'prop3':642}
That is, I want a single object that has all properties. I have a function like this:
Object.values(obj).reduce((acc,currentObj) => {
const props = Object.keys(currentObj);
props.forEach(prop => {
if(prop in acc){
acc[prop] += currentObj[prop]
} else {
acc[prop] = currentObj[prop]
}
})
return acc
}, {})
I believe this works fine, but is there any way to do the same with RamdaJS?
A simple reduce function would do that.
const fn = R.pipe(
R.values,
R.reduce(R.mergeWith(R.add), {}),
);
//--
const data = {
key1: { prop1: 123, prop2: 345 },
key2: { prop1: 673, prop3: 642 },
};
console.log(
fn(data),
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js" integrity="sha256-xB25ljGZ7K2VXnq087unEnoVhvTosWWtqXB4tAtZmHU=" crossorigin="anonymous"></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);

Filter data using lodash

How can filter the data with some inner attribute value.
updated_id='1234';
var result= _.map(self.list, function (item) {
// return only item.User_Info.id=updated_id
});
You can use the lodash#matchesProperty variant of lodash#filter to filter out objects that you need using the path of the property. The variant is in the 3rd example of the lodash#filter documentation.
var result = _.filter(self.list, ['User_Info.id', updated_id]);
var self = {
list: [
{ User_Info: { id: '4321' } },
{ User_Info: { id: '4321' } },
{ User_Info: { id: '1234' } },
{ User_Info: { id: '3214' } },
{ User_Info: { id: '2143' } }
]
};
var updated_id = '1234';
var result = _.filter(self.list, ['User_Info.id', updated_id]);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
lodash has a filter method:
const updated_id='1234';
const result= _.filter(self.list, item => item.User_Info.id === updated_id);
Use lodash _.filter method:
_.filter(collection, [predicate=_.identity])
Iterates over elements of collection, returning an array of all elements predicate returns truthy for. The predicate is invoked with three arguments: (value, index|key, collection).
with predicate as custom function
_.filter(myArr, function(o) {
return o.name == 'john';
});
with predicate as part of filtered object (the _.matches iteratee shorthand)
_.filter(myArr, {name: 'john'});
with predicate as [key, value] array (the _.matchesProperty iteratee shorthand.)
_.filter(myArr, ['name', 'John']);

lodash transform object with array to array of objects

Using a lodash, I want to transform an object that contains array into array of objects. here is an example:
Original object :
[
{
name:"name1"
params: [{param: "value1"}, {param: "value2"}]
},
{
name:"name2"
params: [{param: "value3"}, {param: "value4"}]
}
]
After transformation:
[
{name:"name1", param: "value1"},
{name:"name1", param: "value2"},
{name:"name2", param: "value3"},
{name:"name2", param: "value4"}
]
Whats the easiest way to achieve this ? Thanks
[EDIT]
Till now I implemented the function below, but I'm almost sure there must be more elegant solution for my problem.
transform (res) {
const data = [];
_.each(res, (obj) => {
const params = _.pick(obj, ['params']);
const withoutParams = _.omit(obj, 'params');
_.each(params.params, (param) => {
data.push(_.assign(param, withoutParams));
});
});
console.log('data', data);
return data
}
You can _.map() the params and the name of each object into an array of objects, and then _.flatMap() all objects arrays into one array:
var arr = [
{
name:"name1",
params: [{param: "value1"}, {param: "value2"}]
},
{
name:"name2",
params: [{param: "value3"}, {param: "value4"}]
}
];
var newArr = _.flatMap(arr, function(obj) {
return _.map(obj.params, function(param) {
return {
name: obj.name,
param: param.param
};
});
});
console.log(newArr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
And this is the ES6 version using Array.prototype.map(), arrow functions, and destructuring:
const arr = [
{
name:"name1",
params: [{param: "value1"}, {param: "value2"}]
},
{
name:"name2",
params: [{param: "value3"}, {param: "value4"}]
}
];
const newArr = _.flatMap(arr, ({
name, params
}) => params.map(({
param
}) => ({
name, param
})));
console.log(newArr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>