How do I get a count of this API data? - javascript

I'm making a tags component that takes a count from data that's received from an API. the data looks like something like this:
// Listing.js
const apiData = [
{ id: 1, billing_status: 'NO_ACTION_NEEDED', },
{ id: 2, billing_status: 'COMPLETED', },
{ id: 3, billing_status: 'NO_ACTION_NEEDED', },
{ id: 4, billing_status: 'NEEDS_CODING', },
{ id: 5, billing_status: 'NEEDS_INVOICING', },
{ id: 6, billing_status: 'NEEDS_CODING', },
{ id: 999, billing_status: 'NO_ACTION_NEEDED', },
]
I have a reusable component that I'm trying to construct an array that will resemble something like this while getting the count of all of the billing_status:
// Listing.js
const keys = [
{ label: 'No Action Needed (3)', value: 'NO_ACTION_NEEDED', color: 'red' },
{ label: 'Needs Coding (2)', value: 'NEEDS_CODING', color: 'yellow' },
{ label: 'Needs Invoicing (1)', value: 'NEEDS_INVOICING', color: 'green' },
{ label: 'Completed (1)', value: 'COMPLETED', color: 'blue' },
]
and then passing that into a component that will render said information.
So far I'm passing an object into the reusable component, but i'm a little lost on how to proceed...
// BillingList.js
const filterTags = {
noActionNeeded: 'NO_ACTION_NEEDED'
//.... etc
}
<Listing keys={filterTags} />
can anyone help?

As far as I understand the problem,
First, you can get a count for yourself using the Array.reduce function.
Create a color map for the keys you are expecting.
Use the count and color map variable to get the keys list that you want.
const apiData = [
{ id: 1, billing_status: 'NO_ACTION_NEEDED', },
{ id: 2, billing_status: 'COMPLETED', },
{ id: 3, billing_status: 'NO_ACTION_NEEDED', },
{ id: 4, billing_status: 'NEEDS_CODING', },
{ id: 5, billing_status: 'NEEDS_INVOICING', },
{ id: 6, billing_status: 'NEEDS_CODING', },
{ id: 999, billing_status: 'NO_ACTION_NEEDED', },
]
const keyMap = apiData
.reduce((acc, {billing_status}) => ({
...acc,
[billing_status]: (acc[billing_status] || 0) + 1
}), {})
/*
{
"NO_ACTION_NEEDED": 3,
"COMPLETED": 1,
"NEEDS_CODING": 2,
"NEEDS_INVOICING": 1
}
*/
const colorMap = {
"NO_ACTION_NEEDED": "red",
"COMPLETED": "yellow",
"NEEDS_CODING": "green",
"NEEDS_INVOICING": "blue"
}
const keys = Object.keys(keyMap).map(key => ({
label: `${key} (${keyMap[key]})`,
value: key,
color: colorMap[key]
}))
console.log(keys)

You can separate the variable part and the constant part. No need to create one array that includes both.
For example, the label and the color, which seem fixed for each status, so they could be a constant. Also, to make calling them easier, you can define it like this:
const STATUSES = {
NO_ACTION_NEEDED: { color: 'red', label: 'No Action Needed' },
...
};
Now, all we need to do is compose the API data into a format that could be easily understood by the UI. Let's define the method for it:
const parseData = (apiData) => {
const counts = apiData.reduce((a, c) => {
if (a[c.billing_status]) {
a[c.billing_status]++;
} else {
a[c.billing_status] = 1;
}
}, {});
this.setState({ counts });
};
Now, we can render it like this:
render() {
const { counts } = this.state;
...
{Object.keys(counts).map(key =>
<div color={STATUSES[key].color}>{`${STATUSES[key].label} (${counts[key]})`}</div>
}
...
}

Related

Being able to remove duplicate keys from an array of objects

I have a question about how I can delete the existing elements, for example, in my case "Tallas" is repeated, could you please help me? Thank you very much to those who are willing to help me to solve this problem
const data =
[ { atributos: { Tallas: [{ id: 0, name: 'XS' }, { id: 1, name: 'S' }] }}
, { atributos: { Calzado: [{ id: 0, name: '10' }, { id: 1, name: '9.5' }] }}
, { atributos: { Tallas: [{ id: 0, name: 'XS' }] }}
]
The idea is to have this json format with the last "Tallas" since it is the last one that I added through my dynamic form.
const expected =
[{ atributos: { Calzado: [{ id: 0, name: '10' }, { id: 1, name: '9.5' }] }}
, { atributos: { Tallas: [{ id: 0, name: 'XS' }] }}
]
How do I do this is there a way to do it, I've tried with filter plus the findindex but I can't get to eliminate the repetition of the json res= new.filter((arr, index, self) => index === self.findIndex( (t) => (t.attributes === arr.attributes )))
To unique the array of objects, we can use the Javascript Set module, if the array has complex nested objects, we can stringify each object before creating new Set data. this below function will unique the array of complex objects.
function unique_array(array = []) {
const newSetData = new Set(array.map((e) => JSON.stringify(e)));
return Array.from(newSetData).map((e) => JSON.parse(e));
}
this is a function that takes an array and return the same array but delete every duplicated item
function removeDuplicates(arr) {
return arr.filter((item,
index) => arr.indexOf(item) === index);
}
I didn't understant the part written in spanish so I hope this is what you are looking for
This is a solution specific to your question. this is not a generic solution.
const data = [
{
atributos: {
Tallas: [
{ id: 0, name: "XS" },
{ id: 1, name: "S" },
],
},
},
{
atributos: {
Calzado: [
{ id: 0, name: "10" },
{ id: 1, name: "9.5" },
],
},
},
{
atributos: {
Tallas: [
{ id: 0, name: "XS" },
{ id: 1, name: "S" },
],
},
},
];
function uniqueArray(array) {
const resultObject = array.reduce((acc, eachValue) => {
let keys = Object.keys(eachValue.atributos);
keys.forEach((eachKey) => {
if (!acc[eachKey]) {
acc[eachKey] = [];
}
let list = eachValue["atributos"][eachKey].map(
(each) => each.id + "-" + each.name
);
acc[eachKey].push(...list);
});
return acc;
}, {});
const resultArray = Object.keys(resultObject).reduce((acc, each) => {
let setData = Array.from(new Set(resultObject[each]));
acc.push({
atributos: {
[each]: setData.map((e) => {
return { id: e.split("-")[0], name: e.split("-")[1] };
}),
},
});
return acc;
}, []);
return resultArray;
}
const result = uniqueArray(data)
console.log("result ", JSON.stringify(result, null, 2));

Group array of objects by multiple nested values

I have an array of objects which presents tasks. These tasks are categorized (primary / secondary category).
let tasks = [
{
id: 1,
name: 'Cleanup desk',
primary_category: {
id: 1,
name: 'Indoor'
},
secondary_category: {
id: 2,
name: 'Surfaces'
}
},
{
id: 2,
name: 'Cleanup office floors',
primary_category: {
id: 1,
name: 'Indoor'
},
secondary_category: {
id: 3,
name: 'Ground'
}
},
{
id: 3,
name: 'Water plants',
primary_category: {
id: 2,
name: 'Outdoor'
},
secondary_category: {
id: 3,
name: 'Irrigation'
}
}
];
I now try to create a categories accordion in my frontend and therefore need to group my array differently. The structure should look like:
1) primary category
> secondary category
> tasks
> secondary category
> tasks
2) primary category
> secondary category
> tasks
Therefore I'm trying to achieve a structure similar to this:
let tasks_categorized = [
{
id: 1,
name: 'Indoor',
secondary_categories: [
{
id: 2,
name: 'Surfaces',
tasks: [
{
id: 1,
name: 'Cleanup desk'
}
]
},
{
id: 3,
name: 'Ground',
tasks: [
{
id: 2,
name: 'Cleanup office floors'
}
]
}
]
},
{
id: 2,
name: 'Outdoor',
secondary_categories: [
{
id: 3,
name: 'Irrigation',
tasks: [
{
id: 3,
name: 'Water plants'
}
]
}
]
}
];
I tried using groupBy by lodash but this does not allow grouping by multiple nested key-value pairs. Does anybody know an approach to solve this?
Thank you in advance!
The following provided approach is going to achieve the expected result within a single reduce cycle without any further nested loops.
It does so by implementing a reducer function which creates and/or aggregates at time a prioritized category task while iterating another task array. But most importantly it keeps track of a task item's related primary and secondary categories via a Map based lookup. This lookup reference together with a result array are properties of this function's return value which has to be partly provided as the reduce method's initial value as follows ... { result: [] }.
function createAndAggregatePrioritizedCategoryTask(
{ lookup = new Map, result }, item
) {
const { primary_category, secondary_category, ...taskRest } = item;
const { id: primaryId, name: primaryName } = primary_category;
const { id: secondaryId, name: secondaryName } = secondary_category;
const primaryKey = [primaryId, primaryName].join('###');
const secondaryKey = [primaryKey, secondaryId, secondaryName].join('###');
let primaryCategory = lookup.get(primaryKey);
if (!primaryCategory) {
// create new primary category item.
primaryCategory = {
id: primaryId,
name: primaryName,
secondary_categories: [],
};
// store newly created primary category reference in `lookup`.
lookup.set(primaryKey, primaryCategory);
// push newly created primary category reference to `result`.
result.push(primaryCategory);
}
let secondaryCategory = lookup.get(secondaryKey);
if (!secondaryCategory) {
// create new secondary category item.
secondaryCategory = {
id: secondaryId,
name: secondaryName,
tasks: [],
};
// store newly created secondary category reference in `lookup`.
lookup.set(secondaryKey, secondaryCategory);
// push newly created secondary category reference into the
// `secondary_categories` array of its related primary category.
primaryCategory
.secondary_categories
.push(secondaryCategory);
}
// push the currently processed task-item's rest-data as
// item into the related secondary category's `task` array.
secondaryCategory
.tasks
.push(taskRest);
return { lookup, result };
}
let tasks = [{
id: 1,
name: 'Cleanup desk',
primary_category: { id: 1, name: 'Indoor' },
secondary_category: { id: 2, name: 'Surfaces' },
}, {
id: 2,
name: 'Cleanup office floors',
primary_category: { id: 1, name: 'Indoor' },
secondary_category: { id: 3, name: 'Ground' },
}, {
id: 3,
name: 'Water plants',
primary_category: { id: 2, name: 'Outdoor' },
secondary_category: { id: 3, name: 'Irrigation' },
}];
const { result: tasks_categorized } = tasks
.reduce(createAndAggregatePrioritizedCategoryTask, { result: [] });
console.log({ tasks_categorized });
.as-console-wrapper { min-height: 100%!important; top: 0; }
You could take a dynamic approach with an array of arrays with functions and keys for the nested arrays.
const
tasks = [{ id: 1, name: 'Cleanup desk', primary_category: { id: 1, name: 'Indoor' }, secondary_category: { id: 2, name: 'Surfaces' } }, { id: 2, name: 'Cleanup office floors', primary_category: { id: 1, name: 'Indoor' }, secondary_category: { id: 3, name: 'Ground' } }, { id: 3, name: 'Water plants', primary_category: { id: 2, name: 'Outdoor' }, secondary_category: { id: 3, name: 'Irrigation' } }],
groups = [
[o => o, 'primary category'],
[o => o.primary_category, 'secondary category'],
[o => o.secondary_category, 'tasks']
],
result = tasks.reduce((r, o) => {
groups.reduce((parent, [fn, children]) => {
const { id, name } = fn(o);
let item = (parent[children] ??= []).find(q => q.id === id)
if (!item) parent[children].push(item = { id, name });
return item;
}, r);
return r;
}, {})[groups[0][1]];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

nested for loops to get another object-array output

I need to multiply all the "values" inside "obj1" with the "percent' inside obj2 based on the id of each object. What would be the best way to do that? I've tried with for loop and reduce but I wasn't successful. Any help will be appreciated.
const obj1 = [ { id: 1, value: 10 }, { id: 2, value: 10 } ]
const obj2 = {
len: {
id: 1,
nj: "321345",
percent: 0.05,
},
wor: {
id: 2,
nj: "321345",
percent: 0.1,
}
}
outputExpected: [ { id: 1, value: 0.5 }, { id: 2, value: 1 } ]
You can do that by going through and matching the ids. there are some optimizations that can be made if they are sorted however.
const obj1 = [ { id: 1, value: 10 }, { id: 2, value: 10 } ]
const obj2 = {
len: {
id: 1,
nj: "321345",
percent: 0.05,
},
wor: {
id: 2,
nj: "321345",
percent: 0.1,
}
}
const x = Object.keys(obj2).map((key,index)=>{
const { id, value } = obj1.find(({id})=>id===obj2[key].id)
return ({id,value:value*obj2[key].percent})
})
console.log(x)
//outputExpected: [ { id: 1, value: 0.5 }, { id: 2, value: 1 } ]
You can first create a lookup map using Map, then loop over the obj1 using map to get the desired result
const obj1 = [
{ id: 1, value: 10 },
{ id: 2, value: 10 },
];
const obj2 = {
len: {
id: 1,
nj: "321345",
percent: 0.05,
},
wor: {
id: 2,
nj: "321345",
percent: 0.1,
},
};
const map = new Map();
Object.values(obj2).forEach((v) => map.set(v.id, v));
const result = obj1.map((o) => ({ ...o, value: o.value * map.get(o.id).percent }));
console.log(result);
This should work for you but doesnt handle exeptions if the id doesnt exist in both objects.
// First get the values in an array for easier manipulation
const aux = Object.values(obj2)
const output = obj1.map(ob => {
// Find the id in the other array.
const obj2Ob = aux.find(o => o.id === ob.id) // The id must exist in this aproach
return {
id: ob.id,
value: ob.value * obj2Ob.percent
}
})
console.log(output) // [ { id: 1, value: 0.5 }, { id: 2, value: 1 } ]

create an new array of objects from two array of objects in vanilla JS

I have two array of objects:
watches = [
{
id: "1",
label: "Rolex",
color: "UUID_OF_SILVER",
},
{
id: "2",
label: "Omega",
color: "UUID_OF_BLACK",
},
{
id: "3",
label: "Zenith",
color: "UUID_OF_SILVER",
},
];
watch_colors = [
{
id: "UUID_OF_BLACK",
label: "Black",
},
{
id: "UUID_OF_SILVER",
label: "Silver",
},
];
Now I need to print this in console using vanilla JS
Rolex Silver
Omega Black
Zenith Silver
I think I need to filter these two arrays by the common value in them both which is UUID and then make a new array and map on it to show data, but I am not sure how to approach in code.
This is what I have done so far
function watch() {
watches.map((watch) => {
console.log(watch.label);
});
}
watch();
function color() {
watch_colors.map((color) => {
console.log(color.label);
});
}
color();
Just loop though watches, find the matching color from watch_colors with color value in watches array is same as id value in watch_colors array. i.e, color.id === watch.color
const watches = [
{
id: "1",
label: "Rolex",
color: "UUID_OF_SILVER",
},
{
id: "2",
label: "Omega",
color: "UUID_OF_BLACK",
},
{
id: "3",
label: "Zenith",
color: "UUID_OF_SILVER",
},
];
const watch_colors = [
{
id: "UUID_OF_BLACK",
label: "Black",
},
{
id: "UUID_OF_SILVER",
label: "Silver",
},
];
watches.forEach((watch) => {
const color = watch_colors.find((color) => color.id === watch.color);
if(color) {
console.log(`${watch.label}, ${color.label}`);
}
})
You can make it more efficient using Map to make a dictionary and then loop over watches to print the result.
const watches = [
{
id: "1",
label: "Rolex",
color: "UUID_OF_SILVER",
},
{
id: "2",
label: "Omega",
color: "UUID_OF_BLACK",
},
{
id: "3",
label: "Zenith",
color: "UUID_OF_SILVER",
},
];
const watch_colors = [
{
id: "UUID_OF_BLACK",
label: "Black",
},
{
id: "UUID_OF_SILVER",
label: "Silver",
},
];
const dict = new Map();
watch_colors.forEach(({ id, label }) => dict.set(id, label));
watches.forEach((o) => console.log(`${o.label} ${dict.get(o.color)}`));
You can use the reduce method like the following:
const newWatches = watches.map((val) => {
const color = watch_colors.find((x) => x.id === val.color);
return `${val.label} ${color.label}`;
});
console.log(newWatches);
// prints [ 'Rolex Silver', 'Omega Black', 'Zenith Silver' ]

Rename result property in normalizr

Given data like:
{
id: 1,
ownerName: 'bob',
devices: [
{
id: 2
},
{
id: 3
}
]
}
how would I convert it to the following object
{
result: 1,
entities: {
owners: {
1: {
id: 1,
ownerName: 'bob',
deviceIds: [2, 3]
}
},
devices: {
2: {
id: 2
},
3: {
id: 3
}
}
}
}
using normalizr? I can't figure out how to change devices to deviceIds in the returned result...
You can use the Process Strategy for this. It allows you to manipulate your data before it is processed. Simply return a copy of your object with the keys changed from the processStrategy() method.
const Device = schema.Entity('devices');
const Owner = schema.Entity(
'owners',
{
deviceIds: [ Device ]
},
{
processStrategy: value => ({
id: value.id,
ownerName: value.ownerName,
deviceIds: value.devices
})
}
);

Categories

Resources