multi level groupby ramda js - javascript

I have been trying to use group by at multiple levels so that the object is grouped by tag, folder and report keys. I was able to groupby at single level but could not proceed further.
const data = [{
"_index": "search_index",
"_type": "search_type",
"_id": "Co5EFxnqeo9ruq",
"_score": 1,
"_source": {
"admin_tags": [],
"type": "human",
"folder": "Feature Samples",
"url_t": "url_tdata",
"users": [
128
],
"agent_id": 2,
"url": "url_data",
"favorited_by": [20],
"idx": 121,
"path": "Report Samples//Feature Samples",
"name": "Grouping and Sorting",
"description": ""
}
}];
const mapIndexed = R.addIndex(R.map);
const tg = mapIndexed((x, i) => ({
"name": x._source.admin_tags[0] == undefined ? "Unclasified" : x._source.admin_tags[0],
"idx": i,
"folder": [{
"name": x._source.folder,
"idx": x._source.folder,
"report": [{
"agent_id": x._source.agent_id,
"description": x._source.description,
"favorited_by": x._source.favorited_by[0],
"name": x._source.name,
"idx": x._source.idx,
"path": x._source.path,
"type": x._source.type,
"url": x._source.url,
"url_t": x._source.url_t,
}]
}]
}), data);
const byTag = R.groupBy(data => data.name)
const tagged = byTag(tg);
console.log(tagged)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.24.1/ramda.min.js"></script>
This is the output I am trying to achieve with grouping at three levels and values of user_report_id, views always set to zero.
const output = {
"message": "",
"payload": [{
"name": "Unclasified",
"idx": 0,
"folders": [{
"idx": "Feature Samples",
"name": "Feature Samples",
"reports": [{
"agent_id": 2,
"description": "",
"idx": 121,
"is_fav": 20,
"name": "Grouping and Sorting",
"type": "human",
"url": "url_data",
"url_t": "url_tdata",
"user_report_id": 0,
"views": 0,
"report_id": '2sdfsd'
}]
}]
}]
}

There might be a better/built in solution, but you can use map to group the "inner lists" of your objects like so:
const data = [
{ letter: "a", number: 1, bool: true },
{ letter: "a", number: 2, bool: true },
{ letter: "a", number: 2, bool: false },
{ letter: "b", number: 1, bool: true },
{ letter: "b", number: 1, bool: false },
{ letter: "b", number: 1, bool: false },
{ letter: "c", number: 1, bool: true },
{ letter: "c", number: 2, bool: true },
{ letter: "c", number: 2, bool: false },
{ letter: "c", number: 2, bool: true },
];
const groupByLetterNumberBool = pipe(
groupBy(prop("letter")),
map(groupBy(prop("number"))),
map(map(groupBy(prop("bool"))))
);
groupByLetterNumberBool(data);
Working example here.

Related

how to change the format of json array by loping over

Hi I am getting data from API but I want my data in different format so that I can pass later into a function. I want to change the names of keys into a different one becasuse I have created a chart and it only draws if I send it data in certain way
This is what I am getting from API
data = {
"status": "success",
"from": "DB",
"indice": "KSE100",
"data": [
{
"stock_sector_name": "Tc",
"sector_score": "0828",
"stocks": [
{
"stock_symbol": "TRG",
"stock_score": 44
},
{
"stock_symbol": "SYS",
"stock_score": 33
}
]
},
{
"stock_sector_name": "OIL",
"sector_score": "0828",
"stocks": [
{
"stock_symbol": "FFS",
"stock_score": 44
},
{
"stock_symbol": "SMS",
"stock_score": 33
}
]
},
]
}
But I want my data to look like this like this
data = {
"name": "KSE100",
"children": [
{
"name": "A",
'points': -9,
"children": [
{
"stock_title": "A",
"value": 12,
},
{
"stock_title": "B",
"value": 4,
},
]
},
{
"name": "B",
'points': 20,
"children": [
{
"stock_title": "A",
"value": 12,
},
{
"name": "B",
"value": 4,
},
]
},
]
}
Like I want to replace
stock_sector_name = name
sector_score = value
stocks = children
stock_symbol = name
stock_score = value
I have been trying this for so much time but sill could not figured it out
function convert(d){
return {
name : d.indice,
children : d.data.map(y=>{
return {
name : y.stock_sector_name,
points : y.sector_score,
children : y.stocks.map(z=>{
return {
stock_title: z.stock_symbol,
value : z.stock_score
}
})
}
})
}
}
You can do something like this
const data = {
"status": "success",
"from": "DB",
"indice": "KSE100",
"data": [{
"stock_sector_name": "Tc",
"sector_score": "0828",
"stocks": [{
"stock_symbol": "TRG",
"stock_score": 44
},
{
"stock_symbol": "SYS",
"stock_score": 33
}
]
},
{
"stock_sector_name": "OIL",
"sector_score": "0828",
"stocks": [{
"stock_symbol": "FFS",
"stock_score": 44
},
{
"stock_symbol": "SMS",
"stock_score": 33
}
]
},
]
}
const data2 = {
"name": "KSE100",
"children": [{
"name": "A",
'points': -9,
"children": [{
"stock_title": "A",
"value": 12,
},
{
"stock_title": "B",
"value": 4,
},
]
},
{
"name": "B",
'points': 20,
"children": [{
"stock_title": "A",
"value": 12,
},
{
"name": "B",
"value": 4,
},
]
},
]
}
//stock_sector_name = name
//sector_score = value
//stocks = children
//stock_symbol = stock_title
//stock_score = value
const keys = {
stock_sector_name: "name",
sector_score: "points",
stocks: "children",
stock_symbol: "stock_title",
stock_score: "value",
indice: "name",
//data: "children"
}
const rename = (value) => {
if (!value || typeof value !== 'object') return value;
if (Array.isArray(value)) return value.map(rename);
return Object.fromEntries(Object
.entries(value)
.map(([k, v]) => [keys[k] || k, rename(v)])
);
}
renamedObj = rename(data);
console.log(renamedObj);

How to normalize a nested reponse using normalizr

I would like to normalise a nested JSON data response which I get from the API. The data response contains agents who belong to multiple shifts. The relation between the entities are many-to-many association since, A shift can many agents and an agent can belong to many shifts. So I came across this normalizr utility where I tried to normalize the date to this form so I would be able parse this easier.
I tried to normalize to this form
{
entities : {
shifts:[{...}],
agents:[{...}]
}
}
import { normalize, schema } from 'normalizr';
import stubData from './stubData';
const agent = new schema.Entity('agents', {});
const day_agents = new schema.Entity('day_agents', {
agents: [agent]
});
const shift = new schema.Entity('shifts', {
day_agents
});
const normalizedData = normalize(stubData, shift);
stubData:
"shifts": [{
"id": 1,
"name": "Shift 1",
"start_time": "9:00",
"end_time": "5:00",
"day_agents": {
"10/4/2019": {
"leaves": 1,
"agents": [
{
"id": 1,
"name": "a",
"email": "a#b.co",
"group_id": 1,
"Leave": false
},
{
"id": 2,
"name": "b",
"email": "b#b.co",
"group_id": 1,
"Leave": false
}
]
},
"11/4/2019": {
"leaves": 1,
"agents": [{
"id": 4,
"name": "c",
"email": "c#c.co",
"group_id": 2,
"Leave": true
},
{
"id": 5,
"name": "d",
"email": "d#d.co",
"group_id": 2,
"Leave": false
}
]
}
}
}]

How to convert array of objects to single object which has dynamic key in typescript

This question might be similar to frequently asked one, but this one has some different approach.
In my angular 7 application, I have the following 5 arrays which needs to be converted to the below single object with dynamic key based on the id.
{
"enabled-41": true,
"enabled-42": true,
"enabled-43": true,
"enabled-44": true,
"enabled-45": false,
"abc-41": "some description 1",
"abc-42": "some description 12",
"abc-43": "some description 123",
"abc-44": "some description 1234",
"abc-45": null,
"def-41": "some description 2",
"def-42": "some description 23",
"def-43": "some description 234",
"def-44": "some description 2345",
"def-45": null,
"type-41": "def",
"type-42": "abc",
"type-43": "def",
"type-44": "abc",
"type-45": null,
"weight-41": "25",
"weight-42": "25",
"weight-43": "25",
"weight-44": "25",
"weight-45": null
}
let arr = [
{
"id": 41,
"abc": "some description 1",
"def": "some description 2",
"type": "def",
"Criteria": {
"id": 5,
"question": "follow-up",
"definition": "definition content",
"status": true
},
"weight": 25,
"enabled": true
},
{
"id": 42,
"abc": "some description 12",
"def": "some description 23",
"type": "abc",
"Criteria": {
"id": 1,
"question": "coverage",
"definition": "definition content",
"status": true
},
"weight": 25,
"enabled": true
},
{
"id": 43,
"abc": "some description 123",
"def": "some description 234",
"type": "def",
"Criteria": {
"id": 4,
"question": "Price",
"definition": "definition content",
"status": true
},
"weight": 25,
"enabled": true
},
{
"id": 44,
"abc": "some description 1234",
"def": "some description 2345",
"type": "abc",
"Criteria": {
"id": 3,
"question": "Exchange",
"definition": "definition content",
"status": true
},
"weight": 25,
"enabled": true
},
{
"id": 45,
"Criteria": {
"id": 2,
"definition": "definition conent",
"question": "Random",
"status": true
},
"type": null,
"abc": null,
"def": null,
"weight": 0,
"enabled": false
}
];
let result = arr.reduce(function(obj, item) {
obj[item] = item.value;
return obj;
}, {})
console.log(result);
I have tried using reduce function, but cannot able to get the right way to convert to a single object with the above format based on dynamic key (joining id with hypen).
Can someone help me with this?
You can use reduce with Object.keys, and place all keys you wish to exclude in an array and check against that:
let arr = [{"id":41,"abc":"some description 1","def":"some description 2","type":"def","Criteria":{"id":5,"question":"follow-up","definition":"definition content","status":true},"weight":25,"enabled":true},{"id":42,"abc":"some description 12","def":"some description 23","type":"abc","Criteria":{"id":1,"question":"coverage","definition":"definition content","status":true},"weight":25,"enabled":true},{"id":43,"abc":"some description 123","def":"some description 234","type":"def","Criteria":{"id":4,"question":"Price","definition":"definition content","status":true},"weight":25,"enabled":true},{"id":44,"abc":"some description 1234","def":"some description 2345","type":"abc","Criteria":{"id":3,"question":"Exchange","definition":"definition content","status":true},"weight":25,"enabled":true},{"id":45,"Criteria":{"id":2,"definition":"definition conent","question":"Random","status":true},"type":null,"abc":null,"def":null,"weight":0,"enabled":false}];
let exclude = ["id", "Criteria"];
let result = arr.reduce((acc, curr) => {
let id = curr.id;
Object.entries(curr).forEach(([k, v]) => {
if (!exclude.includes(k)) acc[`${k}-${id}`] = v;
});
return acc;
}, {});
console.log(result);
You code is almost there. But object keys order is not guaranteed. Inside the reduce callback function add the keys in the accumulator and corresponding value.
Use template literals & square notation while creating the object keys
let arr = [{
"id": 41,
"abc": "some description 1",
"def": "some description 2",
"type": "def",
"Criteria": {
"id": 5,
"question": "follow-up",
"definition": "definition content",
"status": true
},
"weight": 25,
"enabled": true
},
{
"id": 42,
"abc": "some description 12",
"def": "some description 23",
"type": "abc",
"Criteria": {
"id": 1,
"question": "coverage",
"definition": "definition content",
"status": true
},
"weight": 25,
"enabled": true
},
{
"id": 43,
"abc": "some description 123",
"def": "some description 234",
"type": "def",
"Criteria": {
"id": 4,
"question": "Price",
"definition": "definition content",
"status": true
},
"weight": 25,
"enabled": true
},
{
"id": 44,
"abc": "some description 1234",
"def": "some description 2345",
"type": "abc",
"Criteria": {
"id": 3,
"question": "Exchange",
"definition": "definition content",
"status": true
},
"weight": 25,
"enabled": true
},
{
"id": 45,
"Criteria": {
"id": 2,
"definition": "definition conent",
"question": "Random",
"status": true
},
"type": null,
"abc": null,
"def": null,
"weight": 0,
"enabled": false
}
];
let result = arr.reduce(function(obj, item) {
obj[`enabled-${item.id}`] = item.enabled;
obj[`abc-${item.id}`] = item.abc;
obj[`def-${item.id}`] = item.def;
obj[`type-${item.id}`] = item.type;
obj[`weight-${item.id}`] = item.weight;
return obj;
}, {});
console.log(result)
Assuming you want to exclude all the properties whose value is an object maybe you can go with this generic idea that uses Object.entries() to traverse the inner objects and some destructuring features.
let arr=[{"id":41,"abc":"some description 1","def":"some description 2","type":"def","Criteria":{"id":5,"question":"follow-up","definition":"definition content","status":true},"weight":25,"enabled":true},{"id":42,"abc":"some description 12","def":"some description 23","type":"abc","Criteria":{"id":1,"question":"coverage","definition":"definition content","status":true},"weight":25,"enabled":true},{"id":43,"abc":"some description 123","def":"some description 234","type":"def","Criteria":{"id":4,"question":"Price","definition":"definition content","status":true},"weight":25,"enabled":true},{"id":44,"abc":"some description 1234","def":"some description 2345","type":"abc","Criteria":{"id":3,"question":"Exchange","definition":"definition content","status":true},"weight":25,"enabled":true},{"id":45,"Criteria":{"id":2,"definition":"definition conent","question":"Random","status":true},"type":null,"abc":null,"def":null,"weight":0,"enabled":false}];
let result = arr.reduce((obj, {id, ...rest}) =>
{
Object.entries(rest).forEach(([k, v]) =>
{
if (Object(v) !== v) obj[`${k}-${id}`] = v;
});
return obj;
}, {});
console.log(result);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
Oh, man... I just got beat. Here's my solution.
let arr= [] // hold the final object array
let keys = [] // temp item to hold the value of each key
// iterate over each key
Object.keys(input).forEach((key) => {
let pair = key.split('-') // split the key into the real key and the index
// if the index isn't in the array, push it there (this keeps the same order)
if (keys.indexOf(pair[1])===-1) {
keys.push(pair[1])
}
// use object.assign to add the keys to the existing object in the right place in the array.
arr[keys.indexOf(pair[1])] = Object.assign({}, arr[keys.indexOf(pair[1])], {[pair[0]]: input[key]}, { id: pair[1] })
})
function getFilteredData(arr) {
const result = {};
arr.forEach(item => {
const { id, Criteria, ...rest } = item;
Object.entries(rest).map(([key, value]) => {
result[`${key}-${id}`] = value;
});
});
return result;
}
let arr = [
{
id: 41,
abc: 'some description 1',
def: 'some description 2',
type: 'def',
Criteria: {
id: 5,
question: 'follow-up',
definition: 'definition content',
status: true
},
weight: 25,
enabled: true
},
{
id: 42,
abc: 'some description 12',
def: 'some description 23',
type: 'abc',
Criteria: {
id: 1,
question: 'coverage',
definition: 'definition content',
status: true
},
weight: 25,
enabled: true
},
{
id: 43,
abc: 'some description 123',
def: 'some description 234',
type: 'def',
Criteria: {
id: 4,
question: 'Price',
definition: 'definition content',
status: true
},
weight: 25,
enabled: true
},
{
id: 44,
abc: 'some description 1234',
def: 'some description 2345',
type: 'abc',
Criteria: {
id: 3,
question: 'Exchange',
definition: 'definition content',
status: true
},
weight: 25,
enabled: true
},
{
id: 45,
Criteria: {
id: 2,
definition: 'definition conent',
question: 'Random',
status: true
},
type: null,
abc: null,
def: null,
weight: 0,
enabled: false
}
];
console.log(getFilteredData(arr));

formatting data into a new structure javascript

I am working with D3 at the moment as an experiment, I recieve data from an API in a certain format, but I need to retructure that data into format that works with D3 below is an example of the data that I am receiving.
{
"user_id": 3,
"service_user": "Phillippa",
"staff_name": "Abbey",
"role": "A",
"staff_id": 2,
"status": "P",
"workbase": "Workbase 1"
},
{
"user_id": 60,
"service_user": "Raymond",
"staff_name": "Adam",
"role": "D",
"staff_id": 8,
"status": "C",
"workbase": "Workbase 2"
},
{
"user_id": 63,
"service_user": "Alison",
"staff_name": "Adam",
"role": "D",
"staff_id": 8,
"status": "C",
"workbase": "Workbase 2"
},
{
"user_id": 68,
"service_user": "Philippa",
"staff_name": "Adam",
"role": "C",
"staff_id": 9,
"status": "C",
"workbase": "Workbase 2"
},
{
"user_id": 57,
"service_user": "Philip",
"staff_name": "Adam",
"role": "W",
"staff_id": 9,
"status": "C",
"workbase": "Workbase 2"
}
The strucutre D3 is expecting is following,
{
"name":"flare",
"children":[
{
"name":"analytics",
"children":[
{
"name":"cluster",
"children":[
{
"name":"AgglomerativeCluster",
"size":3938
},
{
"name":"CommunityStructure",
"size":3812
},
{
"name":"HierarchicalCluster",
"size":6714
},
{
"name":"MergeEdge",
"size":743
}
]
},
{
"name":"graph",
"children":[
{
"name":"BetweennessCentrality",
"size":3534
},
{
"name":"LinkDistance",
"size":5731
},
{
"name":"MaxFlowMinCut",
"size":7840
},
{
"name":"ShortestPaths",
"size":5914
},
{
"name":"SpanningTree",
"size":3416
}
]
},
{
"name":"optimization",
"children":[
{
"name":"AspectRatioBanker",
"size":7074
}
]
}
]
}
]
}
So I need to use the received data to produce a new structure which is basically objects with children arrays.
The way the first structure works is that Workbase 1, has 1 child "Abbey", and in turn "Abbey" has 1 child "Phillipa", now it maybe that Workbase 1 appears many times in the returned data, so all it needs pushing into a Workbase 1 specific object.
Workbase 2 is slightly more complex, Workbase 2 has 1 child "Adam" and "Adam" has 4 children "Raymond", "Allison", "Phillipa" and "Phillip".
Theoretically the data should look like this,
{
"name":"Workbase 1",
"children":[
{
"name":"Abbey",
"children":[
{
"name":"Phillipa"
}
]
}
]
},
{
"name":"Workbase 2",
"children":[
{
"name":"Adam",
"children":[
{
"name":"Raymond"
},
{
"name":"Allison"
},
{
"name":"Phillipa"
},
{
"name":"Phillip"
}
]
}
]
}
So far I am looping through the object and making sure I am only getting unique workbases,
original_data.forEach(function(j){
if(_.indexOf(workbases, j.workbase) < 0) {
workbases.push(j.workbase);
data.push({
name : j.workbase,
children : []
});
}
});
From this point I cannot work out to get the correct children and childrens children into the correct workbase, any ideas?
let sorted = original_data.reduce((acc, cur) => {
/* check if the workbase is already in the array */
let wb = acc.find(wb => wb.name === cur.workbase);
if (wb) {
/* check if staff by that name is already in this workbase */
let staff = wb.children.find(staff => staff.name === cur.staff_name);
if (staff) {
staff.children.push({name: cur.service_user});
} else {
/* if not, instantiate this staff in the array */
wb.push({
name: cur.staff_name,
children: [{name: cur.service_user}]
});
}
} else {
/* if not, instantiate this workbase in the array */
acc.push({
name: cur.workbase,
children: [
{
name: cur.staff_name,
children: [{name: cur.service_user}]
}
]
});
}
return acc;
}, []);
You could use a nested approach with a hash table for the names.
var data = [{ "user_id": 3, "service_user": "Phillippa", "staff_name": "Abbey", "role": "A", "staff_id": 2, "status": "P", "workbase": "Workbase 1" }, { "user_id": 60, "service_user": "Raymond", "staff_name": "Adam", "role": "D", "staff_id": 8, "status": "C", "workbase": "Workbase 2" }, { "user_id": 63, "service_user": "Alison", "staff_name": "Adam", "role": "D", "staff_id": 8, "status": "C", "workbase": "Workbase 2" }, { "user_id": 68, "service_user": "Philippa", "staff_name": "Adam", "role": "C", "staff_id": 9, "status": "C", "workbase": "Workbase 2" }, { "user_id": 57, "service_user": "Philip", "staff_name": "Adam", "role": "W", "staff_id": 9, "status": "C", "workbase": "Workbase 2" }],
result = [];
data.forEach(function (a) {
var check = function (key, target) {
if (!this[key]) {
this[key] = { name: key, children: [] };
target.push(this[key]);
}
}.bind(this);
check(a.workbase, result);
check(a.staff_name, this[a.workbase].children);
this[a.staff_name].children.push({ name: a.service_user });
}, Object.create(null));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Merging two nested JSON objects based on conditionals in javascript

I'm trying to figure out how to merge two JSON objects and create a merged object based on conditionals.
I want to loop through leadlistArray, get the rules.id for each entry and match it against rulesArray. The final outcome should be the finalArray.
leadlistArray = {
"status": "success",
"data": {
"leadlists": [
{
"rules": [
{
"$oid": "53866d4d1fd21c3cc41f52da"
}
],
"name": "List 1: only general rule"
},
{
"rules": [
{
"$oid": "53866d4d1fd21c3cc41f52da"
},
{
"$oid": "53866d9c1fd21c3cc79bf7ce"
},
{
"$oid": "53866d9c1fd21c3cc79bf2cd"
}
],
"name": "List 2: general and web-based rule"
},
{
"rules": [
{
"$oid": "53866d9c1fd21c3cc79bf7ce"
}
],
"name": "List 3: only web-based rule"
}
]
}
}
Rules array:
rulesArray = {
"status": "success",
"data": {
"rules": [
{
"description": "optimizely no!",
"start_time": "",
"values": [
"optimizely"
],
"end_time": "",
"operator": "",
"_id": {
"$oid": "53866d4d1fd21c3cc41f52da"
},
"type": 0,
"condition": 1
},
{
"description": "google_plus no!",
"start_time": "",
"values": [
"google_plus"
],
"end_time": "",
"operator": "",
"_id": {
"$oid": "53866d9c1fd21c3cc79bf2cd"
},
"type": 1,
"condition": 1
},
{
"description": "google_plus yes!",
"start_time": "",
"values": [
"google_plus"
],
"end_time": "",
"operator": "",
"_id": {
"$oid": "53866d9c1fd21c3cc79bf7ce"
},
"type": 1,
"condition": 0
}
]
}
}
the finalArray should, for each list, group the rules descriptions by rule type:
finalArray = [
{'name': 'List 1: only general rule'
,'rules':
{0: [
{'description': 'optimizely no!'}
]}
},
{'name': 'List 2: general and web-based rule'
,'rules':
{0: [
{'description': 'optimizely no!'}
], 1: [
{'description': 'google_plus yes!'},
{'description': 'google_plus no!'}
]}
},
{'name': 'List 3: only web-based rule'
,'rules': {
1: [
{'description': 'google_plus yes!'}
]}
}
]
Try this http://jsfiddle.net/eN52v/
var rules = rulesArray.data.rules
.reduce(function(acc, item){
return acc[item._id.$oid] = {
description: item.description,
type: item.type}, acc;
}, {}), //cache rules by id to avoid array lookups.
finalArray = leadlistArray.data.leadlists.map(function(item) {
return {
name: item.name,
rules: item.rules.reduce(function(acc, rule){ //Why not an array?
var ruleDesc = rules[rule.$oid],
type = ruleDesc.type;
(acc[type] || (acc[type] = [])).push({description: ruleDesc.description});
return acc;}, {})
}});

Categories