Ramda - get array of objects using ids from other array - ramda.js

I have two sets of data and I would like to use the first one to get an array of objects from the second one. I tried to deal with it by myself but I am missing few steps.
Here is set of ids to use:
const idSet = {
"41": {
"id": "41"
},
"42": {
"id": "42"
},
"43": {
"id": "43"
}
}
And here is second set:
const nodes = {
"3": {
"nodeCommentId": 3,
"nodeId": 43,
},
"4": {
"nodeCommentId": 4,
"nodeId": 41
},
"6": {
"nodeCommentId": 6,
"nodeId": 42
},
"7": {
"nodeCommentId": 7,
"nodeId": 44
},
}
I need to search by id and nodeId so I tried to use something like this to get only ids from first set:
const ids = R.compose(
R.values(),
R.pluck('id')
)(idSet)
I also came up with something like: R.filter(R.compose(R.flip(R.contains)(ids), R.prop('nodeId')), nodes);
But then I have nodeId which is a number and not a string plus I need an array of objects without keys.
Desired output:
[
{
nodeCommentId: 3,
nodeId: 43
},
{
nodeCommentId: 4,
nodeId: 41
},
{
nodeCommentId: 6,
nodeId: 42
}
]
Any help will be appreciated.

This is probably too ugly to use, but it might be a start at a nice solution:
const nodesById = (idSet) => {
const ids = map (Number, pluck ('id') (values (idSet)))
return pipe (values, filter (pipe (prop('nodeId'), contains(__, ids))))
}
const idSet = {41: {id: "41"}, 42: {id: "42"}, 43: {id: "43"}}
const nodes = {3: {nodeCommentId: 3, nodeId: 43, }, 4: {nodeCommentId: 4, nodeId: 41}, 6: {nodeCommentId: 6, nodeId: 42}, 7: {nodeCommentId: 7, nodeId: 44}}
console .log (
nodesById (idSet) (nodes)
)
.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 {map, pluck, values, pipe, filter, prop, contains, __} = R </script>
I'm sure that with a little work, we could make this entirely point-free, but I doubt that will help readability.

Transform the idSet to an array of numbers, and then user R.innerJoin to get the items with the matching nodeId:
const { pipe, values, pluck, map, innerJoin, __, curry } = R
const getIds = pipe(values, pluck('id'), map(Number))
const getNodesById = curry((idSet, nodes) =>
pipe(
values,
innerJoin(
({ nodeId }, id) => nodeId === id,
__,
getIds(idSet)
)
)(nodes)
)
const idSet = {41: {id: "41"}, 42: {id: "42"}, 43: {id: "43"}}
const nodes = {3: {nodeCommentId: 3, nodeId: 43, }, 4: {nodeCommentId: 4, nodeId: 41}, 6: {nodeCommentId: 6, nodeId: 42}, 7: {nodeCommentId: 7, nodeId: 44}}
const result = getNodesById(idSet)(nodes)
console.log(result)
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>

Related

Ramda - how to use multiple functions on the same data structure

I am trying to use multiple ramda functions on this example:
const data = {
"tableItems": [
{
"id": 1,
"name": "1",
"startingPoint": true,
"pageNumber": 15,
"nodes": [
100,
200
]
},
{
"id": 2,
"name": "2",
"startingPoint": true,
"pageNumber": 14,
"nodes": [
300,
400
]
}
],
"nodes": [
{
"id": 100,
"tableItemId": 1,
"content": "test"
},
{
"id": 200,
"tableItemId": 1,
"content": "test"
},
{
"id": 300,
"tableItemId": 2,
"content": "test"
},
{
"id": 400,
"tableItemId": 2,
"content": "test"
}
]
}
I am trying to create new JSON which should look like this where nodes array should be filled with another ramda function:
const newJSON = [
{
"id": "chapter-1",
"name": "2",
"nodes": []
},
{
"id": "chapter-2",
"name": "1",
"nodes": []
}
]
I started with:
let chapters = [];
let chapter;
const getChapters = R.pipe(
R.path(['tableItems']),
R.sortBy(R.prop('pageNumber')),
R.map((tableItem) => {
if(tableItem.startingPoint) {
chapter = {
id: `chapter-${chapters.length+1}`,
name: tableItem.name,
nodes: []
}
chapters.push(chapter);
}
return tableItem
})
)
But how to combine getNodes which needs access to the whole scope of data?
I tried pipe but something is not working.
Example:
const getNodes = R.pipe(
R.path(['nodes']),
R.map((node) => {
console.log(node)
})
)
R.pipe(
getChapters,
getNodes
)(data)
Any help would be appreciated.
We could write something like this, using Ramda:
const {pipe, sortBy, prop, filter, map, applySpec, identity, propEq, find, __, addIndex, assoc} = R
const transform = ({tableItems, nodes}) => pipe (
filter (prop ('startingPoint')),
sortBy (prop ('pageNumber')),
map (applySpec ({
name: prop('name'),
nodes: pipe (prop('nodes'), map (pipe (propEq ('id'), find (__, nodes))), filter (Boolean))
})),
addIndex (map) ((o, i) => assoc ('id', `chapter-${i + 1}`, o))
) (tableItems)
const data = {tableItems: [{id: 1, name: "1", startingPoint: true, pageNumber: 15, nodes: [100, 200]}, {id: 2, name: "2", startingPoint: true, pageNumber: 14, nodes: [300, 400]}], nodes: [{id: 100, tableItemId: 1, content: "test"}, {id: 200, tableItemId: 1, content: "test"}, {id: 300, tableItemId: 2, content: "test"}, {id: 400, tableItemId: 2, content: "test"}]}
console .log (transform (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>
First we filter the tableItems to include only those with startingPoint of true, then we sort the result by pageNumber. Then for each, we create name and nodes elements, based on the original data and on a function that maps the node values to the element in the initial nodes property. Finally, for each one, we add the chapter-# id element using addIndex (map).
This works, and is not horrible. It would take a fair bit of work to make this entirely point-free, I believe. And I don't find it worthwhile... especially because this Ramda version doesn't add anything to a simpler vanilla implementation:
const transform = ({tableItems, nodes}) =>
tableItems
.filter (x => x .startingPoint)
.sort (({pageNumber: a}, {pageNumber: b}) => a - b)
.map (({name, nodes: ns}, i) => ({
id: `chapter-${i + 1}`,
name,
nodes: ns .map (n => nodes .find (node => node .id == n)) .filter (Boolean)
}))
const data = {tableItems: [{id: 1, name: "1", startingPoint: true, pageNumber: 15, nodes: [100, 200]}, {id: 2, name: "2", startingPoint: true, pageNumber: 14, nodes: [300, 400]}], nodes: [{id: 100, tableItemId: 1, content: "test"}, {id: 200, tableItemId: 1, content: "test"}, {id: 300, tableItemId: 2, content: "test"}, {id: 400, tableItemId: 2, content: "test"}]}
console .log (transform (data))
.as-console-wrapper {max-height: 100% !important; top: 0}
This works similarly to the above except that it assigns the id at the same time as name and nodes.
I'm a founder of Ramda and remain a big fan. But it doesn't always add anything to vanilla modern JS.
You can use a curried function. Because the pipe will always pipe the result of the previous function call into the next function. You can use R.tap if you want to step over.
However, I guess you want to have the data object and the output of the previous function call both in your getNodes function. In that case you can use a curried function, where you pass the response of the previous function as last parameter.
const getNodes = R.curryN(2, function(data, tableItemList){
console.log(tableItemList) // result of previous function call
return R.pipe(
R.path(['nodes']),
R.map((node) => {
console.log('node:', node);
})
)(data)
})
And use it like:
R.pipe(
getChapters,
getNodes(data)
)(data)
I would split the solution into two steps:
Prepare the tableItems and nodes to the required end state using R.evolve - filter, sort, and then use R.toPairs the tableItems to get an array that includes the index and the object. Group the nodes by id so you can pick the relevant nodes by id in the combine step.
Combine both properties to create the end result by mapping the new tableItems, and using R.applySpec to create the properties.
const {pipe, evolve, filter, prop, sortBy, toPairs, groupBy, map, applySpec, path, flip, pick} = R
const transform = pipe(
evolve({ // prepare
tableItems: pipe(
filter(prop('startingPoint')),
sortBy(prop('pageNumber')),
toPairs
),
nodes: groupBy(prop('id'))
}),
({ tableItems, nodes }) => // combine
map(applySpec({
id: ([i]) => `chapter-${+i + 1}`,
name: path([1, 'name']),
nodes: pipe(path([1, 'nodes']), flip(pick)(nodes)),
}))(tableItems)
)
const data = {tableItems: [{id: 1, name: "1", startingPoint: true, pageNumber: 15, nodes: [100, 200]}, {id: 2, name: "2", startingPoint: true, pageNumber: 14, nodes: [300, 400]}], nodes: [{id: 100, tableItemId: 1, content: "test"}, {id: 200, tableItemId: 1, content: "test"}, {id: 300, tableItemId: 2, content: "test"}, {id: 400, tableItemId: 2, content: "test"}]}
console.log(transform(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>

Apex Line Area chart is not getting displayed on the page in Vuejs

I am stuck on a page where i am not able to display the charts on the page.
To make it simplify what I have done is, here is the code sandbox:
I see there an error in console about the data, I am not sure about it.
https://codesandbox.io/s/compassionate-snyder-bckoq
I want to display the chart like this (as an example), but I am not able to display on the code sandbox
Please help.
The format of series is not aligned with ApexCharts.
You need to transform the data to match with ApexChart format.
Please see the changes in the codesandbox.
https://codesandbox.io/s/small-dew-eztod?file=/src/components/HelloWorld.vue
options: {
// X axis labels
xaxis: {
type: 'date',
categories: ["2021-05-04", "2021-05-05", "2021-05-07"]
},
},
series: [
{
name: "total",
data: [2, 2, 1],
},
{
name: "pending",
data: [0, 1, 0],
},
{
name: "approved",
data: [2, 1, 1],
},
{
name: "rejected",
data: [0, 0, 0],
},
],
Transform data to fit ApexChart
const data = {
"2021-05-04": {
total: 2,
pending: 0,
approved: 2,
rejected: 0,
},
"2021-05-05": {
total: 2,
pending: 1,
approved: 1,
rejected: 0,
},
"2021-05-07": {
total: 1,
pending: 0,
approved: 1,
rejected: 0,
},
};
const xaxis = {
type: "date",
categories: Object.keys(data).map((key) => key), // ['2021-05-04', '2021-05-05', '2021-05-07']
};
let statusObj = [];
for (const dataValue of Object.values(data)) { // get the values from keys '2021-05-04', '2021-05-05' ...
// loop the values, e.g. 1st loop: { total: 2, pending: 0, approved: 2, rejected: 0, }
for (const [key, value] of Object.entries(dataValue)) {
// take 'total' as example, find if statusObj already has { name: 'total', data: [x] }, e.g. statusObj = { name: 'total', data: [1] }
const existingStatusIndex = Object.keys(statusObj).find(
(sKey) => statusObj[sKey].name === key
);
// if yes, return the index of it
if (existingStatusIndex) {
// add new data value to existing data object. e.g. { name: 'total', data: [1, 2] }
statusObj[existingStatusIndex].data.push(value);
continue;
}
// if no, create a new object and add it to statusObj
statusObj.push({
name: key,
data: [value],
});
}
}
Output:
xaxis {
type: 'date',
categories: [ '2021-05-04', '2021-05-05', '2021-05-07' ]
}
statusObj [
{ name: 'total', data: [ 2, 2, 1 ] },
{ name: 'pending', data: [ 0, 1, 0 ] },
{ name: 'approved', data: [ 2, 1, 1 ] },
{ name: 'rejected', data: [ 0, 0, 0 ] }
]

How to merge two element in a array Vue.js

I just get stuck here....
I have get a list from my database with can have same Id_Plane but different DateCreated, Starts, Ends...
How do I compute 2 element with same ID to 1 element with 2 DateCreated, Starts, Ends
Thanks
Here is my data for example
The below code will give you an array with unique 'Id_plane' by keeping the first object data.
let data = [
{
DateCreated: "2020-10-10",
Ends: 15.5,
Id_plane: 1,
Id_sale: 4,
Id_scheduler: 6,
Starts: 7,
__isset_bitfield: 31
},
{
DateCreated: "2020-10-11",
Ends: 17,
Id_plane: 1,
Id_sale: 4,
Id_scheduler: 6,
Starts: 7,
__isset_bitfield: 31
},
{
DateCreated: "2020-10-12",
Ends: 17,
Id_plane: 2,
Id_sale: 4,
Id_scheduler: 6,
Starts: 4.2,
__isset_bitfield: 40
},
{
DateCreated: "2020-10-11",
Ends: 17,
Id_plane: 2,
Id_sale: 4,
Id_scheduler: 6,
Starts: 7,
__isset_bitfield: 31
},
];
data.map((item, index) => {
let matchingItems = data.filter(el => el.Id_plane === item.Id_plane);
matchingItems.map(matchingItem => {
let key = data.indexOf(matchingItem)
if (key !== index) {
data.splice(key, 1);
}
});
});
console.log(data);
From what I understood you want to return a new list based on the id_plane.
let list = [
{
id_plane: 1,
date_created: "2020-10-10"
},
{
id_plane: 1,
date_created: "2020-10-11"
},
{
id_plane: 4,
date_created: "2020-10-12"
},
{
id_plane: 10,
date_created: "2020-10-13"
},
{
id_plane: 1,
date_created: "2020-10-14"
},
{
id_plane: 4,
date_created: "2020-10-15"
}
];
var ids = list.map(e => e.id_plane).filter((x, i, a) => a.indexOf(x) == i);
var arr = {};
ids.forEach(i => {
arr[i] = list.filter(e => {
return e.id_plane === i;
});
});
console.log(arr);

Duplicate objects based on prop list

I have a list of objects such this one:
var original = [
{
prop1: 1,
prop2: 2,
tags: ["tag1", "tag2"]
},
{
prop1: 3,
prop2: 4,
tags: ["tag1", "tag3", "tag4"]
},
{
prop1: 5,
prop2: 6,
tags: ["tag4"]
}
]
I want to duplicate the objects based on tags in order to finally have one object for every tag (also duplicated ones) using ramda.js.
var parsed = [
{
prop1: 1,
prop2: 2,
tags: ["tag1"]
},
{
prop1: 1,
prop2: 2,
tags: ["tag2"]
},
{
prop1: 3,
prop2: 4,
tags: ["tag1"]
},
{
prop1: 3,
prop2: 4,
tags: ["tag3"]
},
{
prop1: 3,
prop2: 4,
tags: ["tag4"]
},
{
prop1: 5,
prop2: 6,
tags: ["tag4"]
}
]
I tried with this function but I think there is some better solution
var f = (a,b) => R.evolve({tags: () => a}, b)
R.unnest(
R.map((v) =>
R.zipWith(f, v.tags, R.repeat(v, v.tags.length))
)(original)
)
You can do it like this:
const dup = pipe(
map(obj => map(tag => merge(obj, {tags: [tag]}), obj.tags)),
flatten
);
Or, perhaps more readably, like this:
const spread = obj => map(
tag => merge(obj, {tags: [tag]}
), obj.tags);
const dup = pipe(
map(spread),
flatten
);
While this probably could be made points-free with enough effort, it would likely be much uglier.
You can see this in action on the Ramda REPL.

Express configure doesn't work

I use
app.configure( function(){
app.use(express.static(__dirname, '/'));
});
then I figured out it's the old version. Then I used this,
var env = process.env.NODE_ENV || 'development';
if ('development' == env) {
app.use(express.static(__dirname, '/'));
}
but it still give me errors. My whole code is below.
var express = require('express'),
app = express();
var env = process.env.NODE_ENV || 'development';
if ('development' == env) {
app.use(express.static(__dirname, '/'));
}
app.get('/people/:id', function(req, res){`
var customerId = parseInt(req.params.id);
var data = {};
for(var i = 0; i < people.length; i++){
if(people[i].id === customerId){
data = people[i];
break;
}
}
res.json(data);
});
app.get('/people', function(req, res){
res.json(people);
});
app.lisen(8080);
console.log('Listening on express port 8080');
var people = [
{id: 1, name: 'John', city: 'Lisbon', gender: 'male', total: '515.561',
orders: [
{id: 1, product: 'Shoes', total: '100'}
]
},
{id: 2, name:'Abbie', city:'Orlando', gender:'female', total:'3445.34',
orders: [
{id: 2, product: 'Shoes', total: '200.561'}
]
},
{id: 3, name:'Will', city:'Houston', gender:'female', total:'98754.00',
orders: [
{id: 1, product: 'Shoes', total: '300.561'},
{id: 3, product: 'Shoes', total: '330.561'}
]
},
{id: 4, name:'Jim', city:'Paris', gender:'male', total:'15.26',
orders: [
{id: 4, product: 'Shoes', total: '400.561'}
]
},
{id: 5, name:'Bryan', city:'Lisbon', gender:'male', total:'515.561',
orders: [
{id: 5, product: 'Shoes', total: '500.561'}
]
},
{id: 6, name:'Agulera', city:'Orlando', gender:'female', total:'3445.34',
orders: [
{id: 6, product: 'Shoes', total: '600.561'}
]
},
{id: 7, name:'Christeen', city:'Houston', gender:'female', total:'98754.00',
orders: [
{id: 7, product: 'Shoes', total: '700.561'}
]
},
{id: 8, name:'Matt', city:'Paris', gender:'male',total:'15.26',
orders: [
{id: 8, product: 'Shoes', total: '800.561'}
]
}
];
I'm quite new to express and I'm doing this by watching a video. so I want to configure it properly and run it. Please help me with this.
spelling error
app.lisen(8080);
--->
app.listen(8080);
second...
global variables are bad strategy, put the var people as a local variable of app:
http://expressjs.com/api.html#app.locals
app.locals.people = "put the people data here";
Lastly, you should put responses and relevant http error codes to diagnose where there are errors:
for example.....
var yes = "yes";
if ( yes !== data.answer) {
console.log(err);
res.status(401).send(err + "error, you do not have authorized access to this data"); }
else {
console.log("success! You are authorized!")
res.status(200).send(data);
};
Sends back json object data and "ok" status code if condition is met;
status codes:
http://www.w3.org/Protocols/HTTP/HTRESP.html