Related
I am trying to turn an array of objects into another array of objects by grouping by a specific value and adding that value as label and taking it out of the object in the new array.
Input: So for instance I have this array of objects:
let tech = [
{ id: 1, grouping: "Front End", value: "HTML" },
{ id: 2, grouping: "Front End", value: "React" },
{ id: 3, grouping: "Back End", value: "Node" },
{ id: 4, grouping: "Back End", value: "PHP" },
];
Expected: I am looking to try and figure out how I can get to this, where there is a label for each of the unique groupings and options array containing the values of that grouping.
[
{
label: "Front End",
options: [
{ id: 1, value: "HTML" },
{ id: 2, value: "React" },
],
},
{
label: "Back End",
options: [
{ id: 3, value: "Node" },
{ id: 4, value: "PHP" },
],
},
]
The closest I have been able to get to is using reduce to group by the grouping key:
const groupedTech = tech.reduce((acc, value) => {
// Group initialization
if (!acc[value.grouping]) {
acc[value.grouping] = [];
}
// Grouping
acc[value.grouping].push(value);
return acc;
}, {});
Which gives me this:
{
"Front End": [
{ id: 1, grouping: "Front End", value: "HTML" },
{ id: 2, grouping: "Front End", value: "React" },
],
"Back End": [
{ id: 3, grouping: "Back End", value: "Node" },
{ id: 4, grouping: "Back End", value: "PHP" },
],
}
But this returns object not an array and doesn't remove the grouping value. I have not been able to figure out how to group properly because in the array of objects I have not found an efficient way to compare against to see if the grouping exists and if so add to that nested array. Would I be better off using something like .map()? Appreciate any leads/learnings!
You're very close, just wrap the key-value entries of the result you've got in a map function:
let tech = [
{ id: 1, grouping: "Front End", value: "HTML" },
{ id: 2, grouping: "Front End", value: "React" },
{ id: 3, grouping: "Back End", value: "Node" },
{ id: 4, grouping: "Back End", value: "PHP" },
];
const groupedTech = Object.entries(
// What you have done
tech.reduce((acc, { id, grouping, value }) => {
// Group initialization
if (!acc[grouping]) {
acc[grouping] = [];
}
// Grouping
// FIX: only pushing the object that contains id and value
acc[grouping].push({ id, value });
return acc;
}, {})
).map(([label, options]) => ({ label, options }));
console.log(groupedTech);
You just have to do one more manipulation with Object.entries and .map
let tech = [
{ id: 1, grouping: 'Front End', value: 'HTML' },
{ id: 2, grouping: 'Front End', value: 'React' },
{ id: 3, grouping: 'Back End', value: 'Node' },
{ id: 4, grouping: 'Back End', value: 'PHP' }
]
const groupedTech = tech.reduce((acc, value) => {
// Group initialization
if (!acc[value.grouping]) {
acc[value.grouping] = []
}
// Grouping
acc[value.grouping].push(value)
return acc
}, {})
const res = Object.entries(groupedTech).map(([label, options]) => ({
label,
options
}))
console.log(res)
A minor variation on the other two answers if you want to get exactly the output you specify:
let tech = [{
id: 1,
grouping: "Front End",
value: "HTML"
},
{
id: 2,
grouping: "Front End",
value: "React"
},
{
id: 3,
grouping: "Back End",
value: "Node"
},
{
id: 4,
grouping: "Back End",
value: "PHP"
},
];
const groupedTech = Object.entries(
tech.reduce((acc, value) => {
// Group initialization
if (!acc[value.grouping]) {
acc[value.grouping] = [];
}
// Grouping
acc[value.grouping].push({
id: acc[value.grouping].length+1,
value: value.value
});
return acc;
}, {}))
.map(([label, options]) => ({
label,
options
}));
console.log(groupedTech);
I usually like to build up a Map of key / value pairs then transform those entries into the final result (usually using Array.prototype.map() or Array.from()).
const tech = [
{ id: 1, grouping: "Front End", value: "HTML" },
{ id: 2, grouping: "Front End", value: "React" },
{ id: 3, grouping: "Back End", value: "Node" },
{ id: 4, grouping: "Back End", value: "PHP" },
];
const groupedMap = tech.reduce((map, { grouping, ...option }) => {
if (!map.has(grouping)) {
map.set(grouping, [])
}
map.get(grouping).push(option)
return map
}, new Map())
const groupedTech = Array.from(groupedMap, ([ label, options ]) => ({
label,
options
}))
console.log(groupedTech)
Using a Map and Map#values()
const grouped = tech.reduce((m,{grouping:label, ...rest})=>{
const group = m.get(label) || {label, options:[]};
group.options.push({...rest})
return m.set(label, group)
},new Map)
console.log([...grouped.values()])
<script>
let tech=[{id:1,grouping:"Front End",value:"HTML"},{id:2,grouping:"Front End",value:"React"},{id:3,grouping:"Back End",value:"Node"},{id:4,grouping:"Back End",value:"PHP"}];
</script>
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>
}
...
}
I am trying to add the objects into the array based on the condition.
My expectation is to add two objects when the condition met but I am getting only the last object getting added (its element is missing).
const country = ‘USA’
citizenArray.push([
{
label: ‘Alex’,
value: ’32’,
},
country === ‘USA’
? ({
label: ‘John’,
value: ’28’,
},
{
label: ‘Miller’,
value: ’40’,
})
: {
label: ‘Marsh’,
value: ’31’,
},
]);
The output I am getting:
[{
label: ‘Alex’,
value: ’32’,
},
{
label: ‘Miller’,
value: ’40’,
}]
Expected:
[{
label: ‘Alex’,
value: ’32’,
},
{
label: ‘John’,
value: ’28’,
},
{
label: ‘Miller’,
value: ’40’,
}]
Could somebody help me point out where I am doing wrong?
Thanks.
In Javascript when you placed comma-separated expressions within parathesis it will execute each(left to right) and will return the result of last.
In your case ({ label: 'John', value: '28',}, { label: 'Miller', value: '40',}) results just the last object { label: ‘Miller’, value: ’40’, } and adds to the array.
To make it work to use an array and then use spread syntax to add them.
const country = 'USA';
const citizenArray = [];
citizenArray.push([{
label: 'Alex',
value: '32',
},
...(country === 'USA' ? [{
label: 'John',
value: '28',
}, {
label: 'Miller',
value: '40',
}] : [{
label: 'Marsh',
value: '31',
}])
]);
console.log(citizenArray);
Just use different logic like so:
const country = "USA";
let citizenArray = [];
citizenArray.push([{ label: "Alex", value: "32" }, ...(country == "USA" ? [{ label: "John", value: "28" }, { label: "Miller", value: "40" }] : [{ label: "Marsh", value: "31" }])]);
console.log(citizenArray);
.as-console-wrapper { max-height: 100% !important; top: auto; }
How about using the Spread ... operator
const myArray = [
...(condition1 ? [item1] : []),
...(condition2 ? [item2] : []),
...(condition3 ? [item3] : []),
];
I have an array.
var tableHeader = [
{
key: 1,
value: 'inputname',
defaultChecked: true,
columnName: 'input.name',
}, {
key: 3,
value: 'callname',
defaultChecked: true,
columnName: 'call.name',
}, {
key: 4,
value: 'rank',
defaultChecked: true,
columnName: 'call.rank',
}, {
key: 5,
value: 'threshold',
defaultChecked: true,
columnName: 'call.threshold',
}, {
key: 9,
value: 'matchname',
defaultChecked: true,
columnName: 'match.name',
},
]
My requirement: I will remove the object having key 3. While I will push the same object to the array it will push to same position as before. The same will happen if I do for other objects.
I just tried in Typescript I dnt know I much it helps to you,I added empty string in removed object place, after that I replaced with original object
let removedObj, removedIndex: any;
this.tableHeader.forEach((ele, i) => {
if (ele.key == 3) {
removedObj = ele; removedIndex = i;
this.tableHeader.splice(i, 1, '');
}
});
this.tableHeader.splice(removedIndex, 1, removedObj);
console.log(this.tableHeader);
to replace the array element use:
TheNewObject = { key: 9,
value: 'matchname',
defaultChecked: true,
columnName: 'match.name',};
tableHeader[3] = TheNewObject;
just like that ,
and to search for object Index you can use the following method :
function getObjectIndex(skey)
{
for (i = 0; i < tableHeader.length; i++) {
obj = tableHeader[i];
if (obj.hasOwnProperty('key') && obj.key == skey) {
return i;
}
}
}
This looks like two distinct problems, one is to filter out an element inside an array by its property, and the second (and slightly trickier to push a new element back in the same place if it has the same key). I think your best bet is to leave .push alone, and instead look into Array.filter and Array.sort after you add a new element (to restore order), Like this:
var tableHeader = [{
key: 1,
value: 'inputname',
defaultChecked: true,
columnName: 'input.name',
}, {
key: 3,
value: 'callname',
defaultChecked: true,
columnName: 'call.name',
}, {
key: 4,
value: 'rank',
defaultChecked: true,
columnName: 'call.rank',
}, {
key: 5,
value: 'threshold',
defaultChecked: true,
columnName: 'call.threshold',
}, {
key: 9,
value: 'matchname',
defaultChecked: true,
columnName: 'match.name',
}, ]
console.log('Original', tableHeader)
//Filter out {key:3}
tableHeader = tableHeader.filter(function(e) {
return e.key !== 3
})
console.log('Filtered', tableHeader)
tableHeader.push({
key: 3,
value: 'callname',
defaultChecked: true,
columnName: 'call.name',
})
tableHeader.sort(function(a, b) {
return a.key - b.key
})
console.log('Resorted', tableHeader)
I'm trying to remove duplicate items using lodash.js but I can't make it works properly.
This is the structure of an object in the array:
{
label: 'tagA',
value: 1
}
So let say I have this array:
var objectsArray = [
{
label: 'tagA',
value: 1
},
{
label: 'tagB',
value: 2
},
{
label: 'tagC',
value: 3
},
{
label: 'tagB',
value: 4
},
{
label: 'tagB',
value: 5
},
];
I made this piece of code with _.uniqBy() function from lodash.js to try to remove the elements of array with the same labels, but it dosn't work as I expected:
var uniq = _.uniqBy(objectsArray, function(o){
return o.label;
});
I based on some sample found here and there and lodash documentation of course but I have a lack of knowledge in this regard so any help it will super appreciate it.
Thanks.
Make sure that you use proper namings, that code works for me:
var arr = [
{
label: 'tagA',
value: 1
},
{
label: 'tagB',
value: 2
},
{
label: 'tagC',
value: 3
},
{
label: 'tagB',
value: 4
},
{
label: 'tagB',
value: 5
},
];
var uniq = _.uniqBy(arr, function(o){
return o.label;
});
console.log(uniq); // >> Returned an array with first 3 objects from array arr
If you want to make sure you can use uniqWith();
This works for me
var data = [
{
label: 'tagA',
value: 1
},
{
label: 'tagB',
value: 2
},
{
label: 'tagC',
value: 3
},
{
label: 'tagB',
value: 4
},
{
label: 'tagB',
value: 5
},
];
var filtered = _.uniqWith(data, function(first, second){
return first.label === second.label
});
I think the second example is just what you need uniqBy:
// The `_.property` iteratee shorthand.
_.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
// → [{ 'x': 1 }, { 'x': 2 }]