Converting an array of objects into a matrix in JavaScript? - javascript

I have an array of objects like this:
"result": [
{
"UserGroupName": "TestCronGroup2",
"CourseTitle": "Test Publish Course 1",
"ComplianceStatus": false
},
{
"UserGroupName": "TestCronGroup2",
"CourseTitle": "test456",
"ComplianceStatus": null
},
{
"UserGroupName": "TestCronGroup2",
"CourseTitle": "retest456",
"ComplianceStatus": null
},
{
"UserGroupName": "TestCronGroup1",
"CourseTitle": "Test Publish Course 1",
"ComplianceStatus": false
},
And I want it to convert it in a matrix form like this:
Compliance Report Matrix
This is my code so far. I am unable to link the values in matrix properly:
showItems = (columns: any) => {
var columnsArr = new Array();
columnsArr = [
{
title: "Course",
field: "CourseTitle",
},
];
Object.keys(this.props.complianceReport).forEach((key, index) => {
var columnsObj = {
title: this.props.complianceReport[key].UserGroupName,
};
columnsArr.push(columnsObj);
});
return (
<div>
<ReportsTable
columns={columnsArr}
tableData={this.props.complianceReport}
/>
</div>
);
};
What I am able to achieve so far:
My Matrix so far
I am new to react and javascript so any kind of help will be appreciated. Thanks!

There are few things you should start to prepare data for react-table output. First thing is to create array of all the columns (course and unique usergroups) and second array of the unique courses.
// prep columns (usergroups unique)
// use Set to aviod duplicates
let userGroups = new Set();
result.forEach(item => userGroups.add(item.UserGroupName));
let columns = Array.from(userGroups)
.sort()
.map(item => {
return {
Header: item,
accessor: item
};
});
// add column: Courses in front
columns.unshift({ Header: "Courses", accessor: "courses" });
// prep courses (unique)
let courseTitles = new Set();
result.forEach(item => courseTitles.add(item.CourseTitle));
const courseTitlesItems = Array.from(courseTitles).sort();
When you have both of these you can iterate through all the courses and check if it is available for specific usergroup.
// prep data
let data = courseTitlesItems.map(courseItem => {
return {
...columns.reduce(
(o, column) =>
Object.assign(o, {
[column.accessor]: testCourse(result, courseItem, column)
}),
{}
),
courses: courseItem
};
});
Use this function to test for data.
function testCourse(result, courseItem, column) {
const foundItem = result.find(
resultItem =>
resultItem.CourseTitle === courseItem &&
resultItem.UserGroupName === column.accessor
);
return foundItem === undefined || foundItem.ComplianceStatus === null
? "-"
: foundItem.ComplianceStatus === false
? "No"
: "Yes";
}
Demo and the playground I used to solve your quiz. Hope you will find it useful!
https://codesandbox.io/s/react-table-vr0ky?file=/src/App.js

Related

How to get the value of the key present inside an object in Javascript

Below is my sample data from which I want to extract the string present inside ResponseVariations array's object : CriterionExpression
Articles":[
"ResponseVariations":[
"CriterionExpression":"User: internal AND Country: CA"
]
]
Code Snippet:
function getChannel(agent){
const channelInfo = agent.Articles;
channelInfo.forEach((ResponseVariations) => {
if(channelInfo.values(CriterionExpression)!=="DEFAULT_SELECTION")
var iterator = channelInfo.values();
console.log(iterator.next().value);
});
But the above criteria is not fitting well to extract information of those criteria in which the String is not DEFAULT_SELECTION.
Any suggestions how to traverse this array object's value ?
The below code worked in order to fetch the key:
const _dir1 = path.resolve(__dirname, `../`);
const _config1 = JSON.parse(fs.readFileSync(path.resolve(_dir, "./export.4862052732962492282_2.json"), "utf-8"));
const filteredArr = {};
_config1.Articles.forEach(el => {
if (el.ResponseVariations && el.ResponseVariations.length > 1 ) {
el.ResponseVariations && el.ResponseVariations.forEach((rv) => {
if(rv.CriterionExpression !== 'DEFAULT_SELECTION') {
console.log('not default', rv);
filteredArr[rv.CriterionExpression]= rv.Text;
project['data']['supportedChannels'].push(filteredArr);
}
})

JavaScript - Targeting an object value to create another variable

So I have an array which looks like this:
[
{ TransactionValues: '50.00' },
{ TransactionValues: '-77.43' },
{ TransactionValues: '-20.23' },
{ TransactionValues: '200.23' }
]
I am trying to find a way to target the monetary value and create a variable based on the sum of these. When I try to target the "50.00" for example I get "Undefined" and it's still an array.
I'm not exactly sure how I can target it specifically, is it possible? Any help would be appreciated
As per the comments here is the full code (be wary I'm still learning so it's not elegant):
var fs = require('fs');
var parse = require('csv-parse');
var transactionValues = []; //Need an array to hold transactions
var currentTrans = [];
var savingsTrans = [];
//constuctor for transactions
function addData (id, accountType, initiatorType, dateTime, transactions) {
var data = {
"AccountID" : id,
"AccountType" : accountType,
"InitiatorType" : initiatorType,
"DateTime" : dateTime,
"TransactionValues" : transactions
}
transactionValues.push(data); //should add a new line
}
function logTrans (accountType, transactions) {
if (accountType == "CURRENT") {
var cTrans = {
"TransactionValues" : transactions
}
currentTrans.push(cTrans);
}
else {
var sTrans = {
"TransactionValues" : transactions
}
savingsTrans.push(sTrans);
}
};
//parses the csv file, loops each row and adds it to the transactionValue array
var parser = parse({columns: true}, function (err, results) {
console.table(results);
for (const row of results) {
addData(row.AccountID, row.AccountType, row.InitiatorType, row.DateTime, row.TransactionValue );
logTrans(row.AccountType, row.TransactionValue);
}
console.log(transactionValues);
console.log(currentTrans);
console.log(savingsTrans);
});
fs.createReadStream(__dirname+'/testData/customer-1234567-ledger.csv').pipe(parser)
not completely following but at the end of the day you have an array like data below.
you can use filter to target the attribute you want.
you can use map to pull out just the values.
you can use reduce to sum them all up.
run the snippet below to see each step
const data = [
{ TransactionValues: '50.00', AccountType: 'CURRENT' },
{ TransactionValues: '-77.43', AccountType: null},
{ TransactionValues: '-20.23', AccountType: 'CURRENT' },
{ TransactionValues: '200.23', AccountType: null }
];
const CurrentTrans = data.filter((x) => x.AccountType === 'CURRENT');
const SavingTrans = data.filter((x) => x.AccountType !== 'CURRENT');
console.log('CurrentTrans');
console.log(CurrentTrans);
console.log('SavingTrans');
console.log(SavingTrans);
const CurrentTransValues = CurrentTrans.map((x) => parseFloat(x.TransactionValues));
const SavingTransValues = SavingTrans.map((x) => parseFloat(x.TransactionValues));
console.log('CurrentTransValues');
console.log(CurrentTransValues);
console.log('SavingTransValues');
console.log(SavingTransValues);
const TotalCurrentValues = CurrentTransValues.reduce((sum, x) => sum + x);
const TotalSavingValues = SavingTransValues.reduce((sum, x) => sum + x);
console.log('TotalCurrentValues');
console.log(TotalCurrentValues.toFixed(2));
console.log('TotalSavingValues');
console.log(TotalSavingValues.toFixed(2));
So I may have fixed it by using parseFloat in my addData and logTrans functions:
function addData (id, accountType, initiatorType, dateTime, transactions) {
var data = {
"AccountID" : id,
"AccountType" : accountType,
"InitiatorType" : initiatorType,
"DateTime" : dateTime,
"TransactionValues" : parseFloat(transactions)
}
transactionValues.push(data); //should add a new line
}
function logTrans (accountType, transactions) {
if (accountType == "CURRENT") {
var cTrans = parseFloat(transactions);
currentTrans.push(cTrans);
}
else {
var sTrans = parseFloat(transactions);
savingsTrans.push(sTrans);
}
};
Now that seems to of worked. So I can use the "Sum values of objects in array" as suggested before. Thank you everyone :)

Compare array objects and show difference

I have two arrays which I want to compare and check if there is an deleted item in one of these arrays. If there is show me the difference (deleted item)
Here is the code below how I would like to achieve this:
var completedList = [{id:1},{id:2},{id:3},{id:4},{id:7},{id:8}];
var invalidList = [{id:3},{id:4},{id:5},{id:6}];
// filter the items from the invalid list, out of the complete list
var validList = completedList.map((item) => {
console.log(item.id)
return item.id;
//console.log(invalidList.id);
}).filter(item => {
Object.keys(invalidList).map(key => {
console.log(invalidList[key].id)
//return !invalidList[key].id.includes(item.id);
});
})
console.log(validList); // Print [1,2,7,8]
// get a Set of the distinct, valid items
var validItems = new Set(validList);
But this returns me a lot of id's how can I map through both array's and filter on object property id? And only show the difference between these array objects.
So basically what I expect is to see the difference between those arrays so log the differences in id's so in this example: 1,2,5,6,7,8
You could take a Set for getting a difference. For getting the differences from each other (a symmetric difference), you need to get both differences.
const
difference = (a, b) => Array.from(b.reduce((s, v) => (s.delete(v), s), new Set(a))),
getId = ({ id }) => id;
var completedList = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 7 }, { id: 8 }],
invalidList = [{ id: 3 }, { id: 4 }, { id: 5 }, { id: 6 }],
complete = completedList.map(getId),
invalid = invalidList.map(getId),
left = difference(complete, invalid),
right = difference(invalid, complete),
result = [...left, ...right]
console.log(result.join(' '));
console.log(left.join(' '));
console.log(right.join(' '));
This should do the trick.
let completedList = [{id:1},{id:2},{id:3},{id:4},{id:7},{id:8}];
let invalidList = [{id:3},{id:4},{id:5},{id:6}];
// filter the items from the invalid list, out of the complete list
let temp1 = completedList.map(e => e.id);
let temp2 = invalidList.map(e => e.id);
let validList = temp1.filter(e => temp2.indexOf(e) === -1);
// find items only in invalidList
let difference = temp2.filter(e => temp1.indexOf(e) === -1);
console.log(validList); // Print [1,2,7,8]
console.log(difference);
I often rely on lodash implementation for comparison.
In lo dash you can get the job done following manner
_.intersectionWith(arr1, arr2, _.isEqual) - For similarity
_.differenceWith(arr1, arr2, _.isEqual) - for differences
This ans is confined to using a util library to get the job done.
If you are looking for the exact algo I would definitely take some time to develop it and reply as a comment to this post .
Thanks
var completedList = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 7 }, { id: 8 }];
var invalidList = [{ id: 3 }, { id: 4 }, { id: 5 }, { id: 6 }];
//get the items that are in the invalid list but not completed list
var filteredList1 = invalidList.filter((invalidListItem) => !completedList.find((item) => item.id === invalidListItem.id));
//get the items that are in the completed list but not in the invalid list
var filteredList2 = completedList.filter((completedListItem) => !invalidList.find((item) => item.id === completedListItem.id));
//join the two arrays
var difference = filteredList1.concat(filteredList2);
//display the merged array and sort
console.log(difference.sort((item1, item2) => { return item1.id > item2.id ? 1 : item1.id < item2.id ? -1 : 0; }));
//outputs 1,2,5,6,7,8

JavaScript - Filter object based on multiple values

I need to filter some data based on multiple values. Language, title and slug
[
{
de: "4567uy55",
en: "654321",
lang: [
{
id: "654321",
language: "English",
title: "Title1"
},
{
id: "4567uy55",
language: "German",
title: "Title2"
}
],
slug: 'some-slug'
},
...
]
What I have now returns all objects which have one or part of the filters(in case title is This is a title, the word this should match), but I need to return objects which have all of them.
I used an object flattner just to get all properties and values in one object, but I can't get it to filter the way I need it.
multiFilter = (arr, filters) => {
console.log(filters)
console.log(arr)
let newArray = []
for (let c of arr) {
let flatCourse = flatten(c)
for (let k in flatCourse) {
const keyArr = k.split('/')
const filterKeys = Object.keys(filters)
Object.keys(filters).map((key) => {
if (keyArr.includes(key)) {
const flatVal = flatCourse[k].toString().toLowerCase()
const filterVal = filters[key].toString().toLowerCase()
console.log(flatVal)
console.log(filterVal)
if (flatVal.includes(filterVal)) {
arr = []
arr.push(c)
newArray.push(c)
}
}
})
}
}
return newArray
}
Filters look like this:
[
language:["English"],
title: ["Some title"],
slug:["some slug"]
]
Instead of mixing for loops and functional chaining you could just go with one of them:
multiFilter = (arr, filters) =>
arr.map(flatten).filter(el => // filter out elements from arr
Object.entries(filters).every(([fKey, fValues]) => // ensure that every key is included in the object
Object.entries(el).some(([oKey, oValue]) =>
oKey.split("/").includes(fKey) && fValues.includes(oValue)// make sure that at least one of the values equals the elements value
)
)
);
arr.filter(course => {
// Returns an array of booleans corresponding to whether or not each filter condition is satisfied
return Object.keys(filters).map(key => {
return filters[key].map(filter => {
// Special case here because lang is an array
if (key == 'language' && course.lang != undefined) {
return course.lang.some(lang => lang[key].includes(filter))
}
if (course[key] == undefined) {
return false
}
return course[key].includes(filter)
}).every(result => result == true)
}).every(result => result == true)
})

Group the same `category` objects [duplicate]

This question already has answers here:
Group array items using object
(19 answers)
Closed 6 years ago.
I'm trying to group the raw data from:
items:
[
{
category: "blog",
id : "586ba9f3a36b129f1336ed38",
content : "foo, bar!"
},
{
category: "blog",
id : "586ba9f3a36b129f1336ed3c",
content : "hello, world!"
},
{
category: "music",
id : "586ba9a6dfjb129f1332ldab",
content : "wow, shamwow!"
},
]
to
[
{
category: "blog",
items:
[
{
id : "586ba9f3a36b129f1336ed38",
content : "foo, bar!"
},
{
id : "586ba9f3a36b129f1336ed3c",
content : "hello, world!"
},
]
},
{
category: "music",
items:
[
{
id : "586ba9a6dfjb129f1332ldab",
content : "wow, shamwow!"
}
]
}
]
The format like this helps me to print the same category data together in the frontend.
The content of the category field is dynamically, so I'm not sure how do I store it to a temporary object and sort them, any thoughts?
(I can't think a better title for the question, please edit if you got a better title.)
You can do it using Array#reduce in one pass:
var items = [{"category":"blog","id":"586ba9f3a36b129f1336ed38","content":"foo, bar!"},{"category":"blog","id":"586ba9f3a36b129f1336ed3c","content":"hello, world!"},{"category":"music","id":"586ba9a6dfjb129f1332ldab","content":"wow, shamwow!"}];
var result = items.reduce(function(r, item) {
var current = r.hash[item.category];
if(!current) {
current = r.hash[item.category] = {
category: item.category,
items: []
};
r.arr.push(current);
}
current.items.push({
id: item.id,
content: item.content
});
return r;
}, { hash: {}, arr: [] }).arr;
console.log(result);
Or the ES6 way using Map:
const items = [{"category":"blog","id":"586ba9f3a36b129f1336ed38","content":"foo, bar!"},{"category":"blog","id":"586ba9f3a36b129f1336ed3c","content":"hello, world!"},{"category":"music","id":"586ba9a6dfjb129f1332ldab","content":"wow, shamwow!"}];
const result = [...items.reduce((r, { category, id, content }) => {
r.has(category) || r.set(category, {
category,
items: []
});
r.get(category).items.push({ id, content });
return r;
}, new Map).values()];
console.log(result);
Personally, without any helper libraries, I'd just do this
var step1 = items.reduce((result, {category, id, content}) => {
result[category] = result[category] || [];
result[category].push({id, content});
return result;
}, {});
var result = Object.keys(step1).map(category => ({category, items: step1[category]}));
Which babel converts to
var step1 = items.reduce(function (result, _ref) {
var category = _ref.category,
id = _ref.id,
content = _ref.content;
result[category] = result[category] || [];
result[category].push({ id: id, content: content });
return result;
}, {});
var result = Object.keys(step1).map(function (category) {
return { category: category, items: step1[category] };
});
So I just solved the question with the following code (jsfiddle):
// Items
// var items = []
// Create an empty object, used to store the different categories.
var temporaryObject = {}
// Scan for each of the objects in the `items` array.
items.forEach((item) =>
{
// Create a category in the teporary object if the category
// hasn't been created.
if(typeof temporaryObject[item.category] === "undefined")
temporaryObject[item.category] = []
// Push the item to the its category of the `temporaryObject`.
temporaryObject[item.category].push({
id : item.id,
content: item.content
})
})
// Create a empty array used to stores the sorted, grouped items.
var newItems = []
// Scan for each of the category in the `temporaryObject`
for(var category in temporaryObject)
{
// Push the new category in the `newItems` array.
newItems.push({
category: category,
items : []
})
// Get the last category index of the `newItems` array,
// so we can push the related data to the related category.
var lastItem = newItems.length - 1
// Scan for the related category in the `temporaryObject` object.
temporaryObject[category].forEach((item) =>
{
// Push the related data to the related category of the `newItems` array.
newItems[lastItem].items.push(item)
})
}
// Prints the sorted, grouped result.
console.log(newItems)

Categories

Resources