Javascript manipulate data into array of object for d3 stacked chart - javascript

I have:
[{ID: "1", Job: "Student", Gender: "Male", Income: 2200},
{ID: "2", Job: "Student", Gender: "Male", Income: 1300},
{ID: "3", Job: "Student", Gender: "Female", Income: 5400},
{ID: "4", Job: "Teacher", Gender: "Female", Income: 4200},
{ID: "5", Job: "Teacher", Gender: "Female", Income: 4000}]
Trying to manipulate into the below format for d3 stacked bar chart (like the data in this link) :
[{name: "Student", Male: 3500, Female: 5400},
{name: "Teacher", Male: 0, Female: 8200}]
I tried all sorts of for-loop and sums and got myself all confused, so I won't bore you with my lengthy inefficient code. How would you do it?

You can use reduce for that.
The reduce method executes a reducer function on each element of the array.
the reducer function's returned value is assigned to the accumulator (res). this value is remembered for each iteration throughout the array. the second value is the current value which we will use to figure out is this a Male/Female and add to the relatively gender income.
const data = [{ID: "1", Job: "Student", Gender: "Male", Income: 2200},
{ID: "2", Job: "Student", Gender: "Male", Income: 1300},
{ID: "3", Job: "Student", Gender: "Female", Income: 5400},
{ID: "4", Job: "Teacher", Gender: "Female", Income: 4200},
{ID: "5", Job: "Teacher", Gender: "Female", Income: 4000}];
var result = [];
data.reduce(function(res, value) {
if (!res[value.Job]) {
res[value.Job] = { name: value.Job, Male: 0, Female: 0 };
result.push(res[value.Job])
}
if (value.Gender === "Male"){
res[value.Job].Male += value.Income;
}
else{
res[value.Job].Female += value.Income;
}
return res;
}, {});
console.log(result)

I would probably do it like this for my own code. One concern might be that the get_totals_by_job function is a bit difficult to understand without some effort.
const data = [{ID: "1", Job: "Student", Gender: "Male", Income: 2200},
{ID: "2", Job: "Student", Gender: "Male", Income: 1300},
{ID: "3", Job: "Student", Gender: "Female", Income: 5400},
{ID: "4", Job: "Teacher", Gender: "Female", Income: 4200},
{ID: "5", Job: "Teacher", Gender: "Female", Income: 4000}];
const get_totals_by_job = (acc, val) => Object.assign(
acc,
{
[val.Job]: Object.assign(
acc[val.Job] || { Male: 0, Female: 0 },
{ [val.Gender]: (acc[val.Job]?.[val.Gender] || 0) + val.Income }
)
}
);
const result = Object
.entries( data.reduce(get_totals_by_job, {}) )
.map( ([name, totals]) => Object.assign({ name }, totals) );
console.log(result);

Related

TypeError:Cannot read properties of undefined (reading 'map') Consider adding an error boundary to your tree to customize error handling behavior

I keep getting this error: TypeError: Cannot read properties of undefined (reading 'map')
Consider adding an error boundary to your tree to customize error handling behavior.
import { useState } from 'react';
import femaleProfile from './images/femaleProfile.jpg';
import maleProfile from './images/maleProfile.jpg';
const Employees = () => {
const { employees, setEmployees } = useState([{
id: 1,
fullName: "Bob Jones",
designation: "JavaScript Developer",
gender: "male",
teamName: "TeamA"
},
{
id: 2,
fullName: "Jill Bailey",
designation: "Node Developer",
gender: "female",
teamName: "TeamA"
},
{
id: 3,
fullName: "Gail Shepherd",
designation: "Java Developer",
gender: "female",
teamName: "TeamA"
},
{
id: 4,
fullName: "Sam Reynolds",
designation: "React Developer",
gender: "male",
teamName: "TeamB"
},
{
id: 5,
fullName: "David Henry",
designation: "DotNet Developer",
gender: "male",
teamName: "TeamB"
},
{
id: 6,
fullName: "Sarah Blake",
designation: "SQL Server DBA",
gender: "female",
teamName: "TeamB"
},
{
id: 7,
fullName: "James Bennet",
designation: "Angular Developer",
gender: "male",
teamName: "TeamC"
},
{
id: 8,
fullName: "Jessica Faye",
designation: "API Developer",
gender: "female",
teamName: "TeamC"
},
{
id: 9,
fullName: "Lita Stone",
designation: "C++ Developer",
gender: "female",
teamName: "TeamC"
},
{
id: 10,
fullName: "Daniel Young",
designation: "Python Developer",
gender: "male",
teamName: "TeamD"
},
{
id: 11,
fullName: "Adrian Jacobs",
designation: "Vue Developer",
gender: "male",
teamName: "TeamD"
},
{
id: 12,
fullName: "Devin Monroe",
designation: "Graphic Designer",
gender: "male",
teamName: "TeamD"
}]);
return (
<main className='container'>
<div class='row justify-content-center mt-3 mb-3'>
<div class='col-8'>
{
employees.map((employee) => (
<div id={employee.id} className='card'>
<img src={femaleProfile} className='card-img-top' />
<div className='card-body'>
<h5 className='card-title'>Full Name: {employee.fullName}</h5>
<p className='card-text'><b>Designation:</b> {employee.designation}</p>
</div>
</div>
))
}
</div>
</div>
</main >
)
}
export default Employees
I keep getting this error:TypeError:Cannot read properties of undefined (reading 'map')
Consider adding an error boundary to your tree to customize error handling behavior.
useState returns an array, not an object
Use this:
const [ employees, setEmployees ] = useState(/* */)
not this:
const { employees, setEmployees } = useState(/* */)

Optimal way to find union of two data sets

I am working on a side project where I am comparing two different databases and want to find the common elements of the data sets based on the "id" field. I want to know if there is an optimal solution instead of using two nested for loops. Is there a way to do it with a hash map? Many Thanks!
Below is the sample code I am working with.
UPDATE all ids are unique with no possibility of there being a duplicate
// data set 1
const set1 = [
{
id: "001",
name: "bob",
age: "50",
location: "texas"
},
{
id: "002",
name: "bill",
age: "51",
location: "texas"
},
{
id: "003",
name: "ben",
age: "52",
location: "texas"
},
{
id: "004",
name: "cam",
age: "53",
location: "texas"
},
{
id: "005",
name: "max",
age: "54",
location: "texas"
}
]
// data set 2
const set2 = [
{
id: "001",
name: "bob"
},
{
id: "002",
name: "bill"
}
]
// I want to create a function where I find the the common elements of the two lists based on id and put the common element of data set 1 into a list and return that list
const findUnion(set1, set2) {
// logic here, I know I can do a nested for loop but is there a more efficient way such as
// using a hashmap? ( Map() object? )
}
// desired output
const output = [
{
id: "001",
name: "bob",
age: "50",
location: "texas"
},
{
id: "002",
name: "bill",
age: "51",
location: "texas"
}
]
You can use Sets for efficient lookup:
const ids1 = new Set(set1.map(({id}) => id));
const ids2 = new Set(set2.map(({id}) => id));
const output = set1.filter(({id}) => ids1.has(id) && ids2.has(id));
console.log(output);
First of all, you're looking for the intersection, not the union.
As others have said, we can use a Set to track uniqueness. This gives us near O(1) lookup time, and allows us algorithm that runs in something like O(m + n) time where m and n are the sizes of your sets:
const intersection = (s1, s2, ids = new Set (s2 .map (x => x .id))) =>
s1 .filter (({id}) => ids .has (id))
const set1 = [{id: "001", name: "bob", age: "50", location: "texas"}, {id: "002", name: "bill", age: "51", location: "texas"}, {id: "003", name: "ben", age: "52", location: "texas"}, {id: "004", name: "cam", age: "53", location: "texas"}, {id: "005", name: "max", age: "54", location: "texas"}]
const set2 = [{id: "001", name: "bob"}, {id: "002", name: "bill"}]
console .log (intersection (set1, set2))
.as-console-wrapper {max-height: 100% !important; top: 0}
First we combine into one long array. Then group by id using reduce method. Each group contains the item and count of appearances. Finally, for each of the groups, return only those with count of appearances > 1.
Edit: fixed algorithm see code.
Edit 2: Made it more generic so order of items won't matter. This is by extending the duplicates rather then replacing them.
function findUnion(set1, set2) {
// first remove duplicates from each set
// bonus: collect duplicates
var duplicates;
function dedup(set) {
duplicates = []
return Object.values(set.reduce(function(agg, item) {
var merged = item;
if (agg[item.id]) {
merged = { ...agg[item.id],
...item
}
duplicates.push(merged)
}
agg[item.id] = merged;
return agg
}, {}));
}
set1 = dedup(set1);
set2 = dedup(set2);
// then combine
var combined = [...set1, ...set2]
// then remove duplicates again, this time keep them
dedup(combined)
return duplicates;
}
// data set 1
const set1 = [{
id: "001",
name: "bob",
age: "50",
location: "texas"
},
{
id: "002",
name: "bill",
age: "51",
location: "texas"
},
{
id: "003",
name: "ben",
age: "52",
location: "texas"
},
{
id: "004",
name: "cam",
age: "53",
location: "texas"
},
{
id: "005",
name: "max",
age: "54",
location: "texas"
},
{
id: "005",
name: "max",
age: "54",
location: "texas"
},
{
id: "005",
name: "max",
age: "54",
location: "texas"
}
]
// data set 2
const set2 = [{
id: "001",
name: "bob"
},
{
id: "002",
name: "bill"
}
]
// desired output
const output = [{
id: "001",
name: "bob",
age: "50",
location: "texas"
},
{
id: "002",
name: "bill",
age: "51",
location: "texas"
}
]
console.log(findUnion(set1, set2))

How to move the objects to the top inside array based on the matched key

I have an array of objects with different keys I want to move the objects to the top based on the key I want.
Given array based on the status: "Active" I want all the matched objects to be moved top
[
{name: "abc", age: "20", status: "Active"},
{name: "xyz", age: "21", status: "Inactive"},
{name: "pqr", age: "22", status: "Active"}
]
Expected Output:
[
{name: "abc", age: "20", status: "Active"},
{name: "pqr", age: "22", status: "Active"},
{name: "xyz", age: "21", status: "Inactive"}
]
let list = [{
name: "abc",
age: "20",
status: "Active"
}, {
name: "xyz",
age: "21",
status: "Inactive"
}, {
name: "pqr",
age: "22",
status: "Active"
}]
list.sort((a, b) => (a.status !== "Active") ? 1 : -1)
console.log(list)
Produces the following output
[
{ name: 'pqr', age: '22', status: 'Active' },
{ name: 'abc', age: '20', status: 'Active' },
{ name: 'xyz', age: '21', status: 'Inactive' }
]
you can change your code like this.
function handleData(arr, status) {
if(!arr || arr.length == 0) return
for(let i=0; i<arr.length; i++) {
let item = arr[i];
if(item.status == status) {
arr.splice(i, 1);
arr.unshift(item)
}
}
}
let arr = [{name: "abc", age: "20", status: "Active"}, {name: "xyz", age: "21", status: "Inactive"}, {name: "pqr", age: "22", status: "Active"}]
handleData(arr, 'Active')
Arrays have method Array.sort where you can pass function that return 1, -1 or 0 which corresponds to how to move element of an array
const arr = [{
name: "abc",
age: "20",
status: "Active"
}, {
name: "xyz",
age: "21",
status: "Inactive"
}, {
name: "pqr",
age: "22",
status: "Active"
}]
console.log(arr.sort((a, b) => (a.status === b.status) ? 0 : a.status === "Active" ? -1 : 1))

Flatten array with multiple objects with nested objects

this particular desired outcome I'm trying to do is turning out to be a bit more challenging that I had expected for someone who is just starting out programming.
I'm storing the results of an API query in an array variable that looks like this:
[{
balance: 4444,
playerInfo: {
age: "18",
gender: "Male",
level: "2",
name: "Joe"
}
}, {
balance: 3333,
playerInfo: {
age: "45",
gender: "Male",
level: "3",
name: "Angel"
}
}, {
balance: 2222,
playerInfo: {
age: "20",
gender: "Female",
level: "11",
name: "Luce"
}
}]
My desired outcome is:
[{
balance: 4444,
level: "2",
name: "Joe"
}, {
balance: 3333,
level: "3",
name: "Angel"
}, {
balance: 2222,
level: "11",
name: "Luce"
}]
I've had some minor progress with flat and flatMap but not entirely sure if its the right way to go for compatibility since the intended target group may be using outdated browsers.
The logic for some other answers are a bit tough for me to grasp atm so would appreciate a few pointers in case
Thank you!
You can make use of Array.map and Object destructuring.
let data = [{balance:4444,playerInfo:{age:"18",gender:"Male",level:"2",name:"Joe"}},{balance:3333,playerInfo:{age:"45",gender:"Male",level:"3",name:"Angel"}},{balance:2222,playerInfo:{age:"20",gender:"Female",level:"11",name:"Luce"}}]
const formatData = (data) => {
return data.map(({balance, playerInfo}) => ({
balance,
level: playerInfo.level,
name: playerInfo.name
}))
}
console.log(formatData(data))
let data = [{balance:4444,playerInfo:{age:"18",gender:"Male",level:"2",name:"Joe"}},{balance:3333,playerInfo:{age:"45",gender:"Male",level:"3",name:"Angel"}},{balance:2222,playerInfo:{age:"20",gender:"Female",level:"11",name:"Luce"}}]
const formatData = (data) => {
return data.map(({ balance, playerInfo: { level, name }}) => ({
balance,
level,
name
}))
}
console.log(formatData(data));
You can directly use map method to transform.
let input = [{
balance: 4444,
playerInfo: {
age: "18",
gender: "Male",
level: "2",
name: "Joe"
}
}, {
balance: 3333,
playerInfo: {
age: "45",
gender: "Male",
level: "3",
name: "Angel"
}
}, {
balance: 2222,
playerInfo: {
age: "20",
gender: "Female",
level: "11",
name: "Luce"
}
}];
let output = input.map(obj => ({
balance: obj.balance,
level: obj.playerInfo?.level,
name: obj.playerInfo?.name,
}));
console.log(output);
Something short 'n sweet is this:
let rawData = [{
balance: 4444,
playerInfo: {
age: "18",
gender: "Male",
level: "2",
name: "Joe"
}
}, {
balance: 3333,
playerInfo: {
age: "45",
gender: "Male",
level: "3",
name: "Angel"
}
}, {
balance: 2222,
playerInfo: {
age: "20",
gender: "Female",
level: "11",
name: "Luce"
}
}]
let formattedData =
rawData.map(({
balance,
playerInfo: {
level,
name
}
}) => ({ balance, level, name }))
console.log(formattedData)

Filter a list based on a selection

Given the data below, I have two select lists, the first select list is to display the name of each person... the second select list is two display the name of the children of the selected person. Using lodash what is the easiest way to do this?
const people = [{
id: "1",
name: "bob",
gender: "male",
children: [{
id: "1",
name: "sarah"
}]
},
{
id: "2",
name: "tom",
gender: "male",
children: [{
id: "1",
name: "lisa"
}]
},
{
id: "3",
name: "sue",
gender: "female",
children: [{
id: "1",
name: "larry"
}]
}
]
Please find the solution as below:
import map from "lodash/map";
import partialRight from "lodash/partialRight";
import pick from "lodash/pick";
import find from "lodash/find";
const test = [
{
id: "2",
name: "tom",
gender: "male",
children: [
{
id: "1",
name: "lisa"
}
]
},
{
id: "3",
name: "sue",
gender: "female",
children: [
{
id: "1",
name: "larry"
}
]
}
];
// Person selection list
const persons = map(test, partialRight(pick, ["id", "name", "gender"]));
// Replace selected person value in `persons[0]`.
const childrens = find(test, item => item.id === persons[0].id).children;

Categories