Array of objects to objects with properties as keys - javascript

I need some help to get objects from an array of object with keys coming from nested properties of the objects in the initial array. This is the initial array :
[
{
"id":{
"colName":"columnA",
"recId":"123"
},
"desc":"this is a description for A",
"resCode":"-1"
},
{
"id":{
"colName":"columnB",
"recId":"123"
},
"desc":"this is a description for B",
"resCode":"-1"
},
{
"id":{
"colName":"columnC",
"recId":"234"
},
"desc":"description for column c ",
"resCode":"-1"
}
];
And my desired output would be this:
{
123: {
columnA: {
desc: "this is a description for A",
rescode: "-1"
}
columnB: {
desc: "this is a description for B",
rescode: "-1"
}
},
234: {
columnC: {
desc: "description for column c ",
resCode: "-1",
}
}
}
I tried to use reduce to do so but I have an issue. I don't know how (and when) to "clear" the temp variable so I can only have column names belonging to one recId.
const initialArray = [
{
"id": {
"colName": "columnA",
"recId": "123"
},
"desc": "this is a description for A",
"resCode": "-1"
},
{
"id": {
"colName": "columnB",
"recId": "123"
},
"desc": "this is a description for B",
"resCode": "-1"
},
{
"id": {
"colName": "columnC",
"recId": "234"
},
"desc": "description for column c ",
"resCode": "-1"
}
];
let temp = {};
const mappedObj = initialArray.reduce((obj, item) => {
temp[item.id.colName] = Object.assign({}, {desc: item.desc}, {resCode: item.resCode} );
obj[item.id['recId']] = Object.assign({}, temp);
return obj;
}, {});
console.log(mappedObj);

You don't need to maintain a temp variable outside of reduce, you can simply process the same actions in reduce itself
const initialArray = [
{
"id": {
"colName": "columnA",
"recId": "123"
},
"desc": "this is a description for A",
"resCode": "-1"
},
{
"id": {
"colName": "columnB",
"recId": "123"
},
"desc": "this is a description for B",
"resCode": "-1"
},
{
"id": {
"colName": "columnC",
"recId": "234"
},
"desc": "description for column c ",
"resCode": "-1"
}
];
const mappedObj = initialArray.reduce((obj, item) => {
obj[item.id.recId] = {...(obj[item.id.recId] || {}), [item.id.colName]: {desc: item.desc, resCode: item.resCode}}
return obj;
}, {});
console.log(mappedObj);

Related

How to convert array of object into nested array of object in Javascript?

I am having an array of objects like this
[
{
name: "dhanush",
goals: ["goal 1","goal 2"],
goalAmount: ["10000","20000"]
},
{
name: "kumar",
goals: ["goal 3", "goal 4"],
goalAmount: ["30000","40000"]
},
{
name: "test",
goals: ["goal 5"],
goalAmount: ["50000"],
}
]
Is that possible to convert the above array of object into the below structure like this
[
{
"name": "dhanush",
"goals": "---",
"goalAmount":"---",
"subRows": [
{
"name": "",
"goals": "goal 1",
"goalAmount":" 10000"
},
{
"name": "",
"goals": "goal 2",
"goalAmount":" 20000"
}
]
},
{
"name": "kumar",
"goals": "---",
"goalAmount":"---",
"subRows": [
{
"name": "",
"goals": "goal 3",
"goalAmount":" 30000"
},
{
"name": "",
"goals": "goal 4",
"goalAmount":" 40000"
}
]
},
{
"name": "Test",
"goals": "goal 5",
"goalAmount":"50000"
}
]
In the first data, you can see the user has multiple goals (which means the array length is more than 1), If the goal length is more than one, need to create another key and move the goal data into the above structure like this.
Why I am doing this because I got an ask to create a table that needs to support expandable rows. I used #tanstack/react-table for this row expansion. Here you can find the working demo link - https://codesandbox.io/s/tanstack-table-expansion-1t77ks?file=/src/App.js
In the demo, you can see the row can expand. For the expansion that table requires the data format like this.
I tried to do something like this,
var data = [
{
name: "dhanush",
goals: ["goal 1","goal 2"]
},
{
name: "kumar",
goals: ["goal 3", "goal 4"]
},
{
name: "test",
goals: ["goal 5"]
}
]
let result = data.map((val,i) => {
return {
name: val.name,
...(val.goals.length === 1 && {goals: val.goals[0]}),
[val.goals.length > 1 && 'subRows']: data.map((t,j) => {
return{
name: "",
goals: val.goals[j]
}
}),
}
})
But the output I am getting like this instead of the actual structure
[
{
"name": "dhanush",
"subRows": [
{
"name": "",
"goals": "goal 1"
},
{
"name": "",
"goals": "goal 2"
},
{
"name": ""
}
]
},
{
"name": "kumar",
"subRows": [
{
"name": "",
"goals": "goal 3"
},
{
"name": "",
"goals": "goal 4"
},
{
"name": ""
}
]
},
{
"name": "test",
"goals": "goal 5",
"false": [
{
"name": "",
"goals": "goal 5"
},
{
"name": ""
},
{
"name": ""
}
]
}
]
Could you please help to achieve this?
This here
[val.goals.length > 1 && 'subRows']: data.map((tm j) => {
evaluates to false if there's 1 goal or less, and results in the string property false. Then you're mapping the whole data again inside that for some reason. Only map over the goals of the current element you're iterating over, the val.goals.
Because the different possible resulting objects are pretty differently strucured, I think this would be easier to manage if they were entirely separate - return { name, goals: goals[0] } if there's only one goal, and return a completely different object mapping over the goals otherwise.
var data=[{name:"dhanush",goals:["goal 1","goal 2"],goalAmount:["10000","20000"]},{name:"kumar",goals:["goal 3","goal 4"],goalAmount:["30000","40000"]},{name:"test",goals:["goal 5"],goalAmount:["50000"]}];
const result = data.map(({ name, goals, goalAmount }) => {
return goals.length === 1
? { name, goals: goals[0], goalAmount: goalAmount[0] }
: {
name,
goals: '---',
subRows: goals.map(
(goal, i) => ({ name: '', goal, goalAmount: goalAmount[i] })
)
};
});
console.log(result);
Don't try to do this all in a single object literal, it's a confusing way to create properties conditionally. Just write normal conditional statements.
To get the goalAmount, use the index argument to the map() callback function so you can get the corresponding element in another array.
var data = [
{
name: "dhanush",
goals: ["goal 1","goal 2"],
goalAmount: ["10000","20000"]
},
{
name: "kumar",
goals: ["goal 3", "goal 4"],
goalAmount: ["30000","40000"]
},
{
name: "test",
goals: ["goal 5"],
goalAmount: ["50000"],
}
]
var result = data.map(({
name,
goals,
goalAmount
}) => {
let item = {
name
};
if (goals.length == 1) {
item.goals = goals[0];
item.goalAmount = goalAmount[0];
} else {
item.goals = "---";
item.subRows = goals.map((g, i) => ({
name: "",
goals: g,
goalAmount: goalAmount[i]
}));
}
return item;
});
console.log(result);

Find and clone and add the clone after the found values with javascript in a object of arrays

I am trying to find a match in a object of arrays and clone this, change the ID and insert this after the found match.
Each plan has clusters and each cluster has goals, the idea is that I need to clone a goal and insert this AFTER the cloned goal (it will be loaded below this goal in the UI).
Main structure
{
"id": 100,
"title": "Plan ABC",
"clusters": [
{
"id": 1,
"subject": "Some subject",
"goals": [
{
"id": 1,
"title": "Goal A",
},
{
"id": 2,
"title": "Goal B",
},
{
"id": 3,
"title": "Goal C",
},
],
},
{
"id": 2,
"subject": "Some subject",
"goals": [
{
"id": 4,
"title": "Goal D",
},
{
"id": 5,
"title": "Goal E",
},
{
"id": 6,
"title": "Goal F",
},
],
},
]
}
My test code
// this would not work ofcourse!
const newId = 12345;
const matchToId = 2;
plan.clusters?.map(cluster => {
cluster?.goals?.map((goal, i) => {
if (goal.id === matchToId) {
// will copy goal with id 2
const copyGoal = goal;
return {...goal, ...copyGoal};
}
return {...goal};
});
// this will work but it will change the id but not copy and add a the new object
plan.clusters = clusters.map(cluster => {
return {
...cluster,
goals: cluster.goals?.filter(goal => {
if (itemId == goal.id) {
const cloned = goal;
cloned.id = 12345;
return {...goal, cloned};
}
return goal;
}),
};
});
What I want
{
"id": 100,
"title": "Plan ABC",
"clusters": [
{
"id": 1,
"subject": "Some subject",
"goals": [
{
"id": 1,
"title": "Goal A",
},
{
"id": 2,
"title": "Goal B",
},
// this will be added
{
"id": 12345,
"title": "COPIED GOAL",
},
// ---
{
"id": 3,
"title": "Goal C",
},
],
},
{
"id": 2,
"subject": "Some subject",
"goals": [
{
"id": 4,
"title": "Goal D",
},
{
"id": 5,
"title": "Goal E",
},
{
"id": 6,
"title": "Goal F",
},
],
},
]
}
This may be one possible solution to achieve the desired objective.
Code Snippet
// check if "tgtId" is present in "goals" array (parameter: "arr")
// and if so, insert "newObj" (parameter: "obj") into the array
const copyObjToGoal = (arr, tgtId, obj) => {
let resObj = {goals: arr}; // the default "result object"
const tgtIdx = arr.findIndex( // search for "tgtId"
({ id }) => id === tgtId
);
if (~tgtIdx) { // if tgtIdx is not -1 (ie, tgtId was found in arr)
const newArr = [ // non-shallow-copy of "arr" (to prevent mutation)
...arr.map(
x => ({...x})
)
]; // mutate the copied "newArr", not the parameter "arr"
newArr.splice(
tgtIdx + 1, // add "after" the "tgtId" position in "goals" array
0, // 0 indicates not to remove any element
{...obj} // destructure to shallow-copy the "newObj" object
);
resObj = { // update the resObj by re-assigning
goals: [...newArr]
};
};
return resObj; // return the result-object
};
// search and insert new/copied goal (not mutating 'myObj')
const searchAndInsert = (tgtId, newObj, obj) => (
Object.fromEntries( // transform below result back into object
Object.entries(obj) // iterate key-value pairs of "obj"
.map(([k, v]) => {
if (k !== 'clusters') { // if key is not "clusters", no change
return [k, v];
} else { // else (for "clusters"
return [ // transform the "goals" array for each cluster
k, // where "tgtId" is found
v.map( // iterate over array of "clusters"
({goals, ...r1}, idx) => {
return { // return each "clusters" array object
...r1,
...copyObjToGoal(goals, tgtId, newObj)
}
}
)
];
}
})
)
);
const myObj = {
"id": 100,
"title": "Plan ABC",
"clusters": [
{
"id": 1,
"subject": "Some subject",
"goals": [
{
"id": 1,
"title": "Goal A",
},
{
"id": 2,
"title": "Goal B",
},
{
"id": 3,
"title": "Goal C",
}
]
},
{
"id": 2,
"subject": "Some subject",
"goals": [
{
"id": 4,
"title": "Goal D",
},
{
"id": 5,
"title": "Goal E",
},
{
"id": 6,
"title": "Goal F",
}
]
}
]
};
const targetGoalId = 2;
const newGoalObj = { id: '12345', title: 'copied goal' };
console.log(
'Test case 1, from question\n',
searchAndInsert(targetGoalId, newGoalObj, myObj)
);
console.log(
'Test case 2, bespoke\n',
searchAndInsert(6, { id: '12345', title: 'copied again' }, myObj)
);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Explanation
Inline comments in the above snippet describe the significant aspects of the solution.
Notes
This solution employs below JavaScript features:
Array .findIndex()
... spread
Array .splice()
Array .map()
Object.fromEntries()
Object.entries()
De-structuring

How to reformat object using its property value as property key?

how can I assign object property value as property key?
I have a set of data:
const mydata = [
{
"id": 001,
"value": "Value 1",
"title": "Title 1"
},
{
"id": 002,
"value": [
{
"Name": "Name 1",
"Age": "20"
},
{
"Name": "Name 2",
"Age": "30"
},
],
"title": "Title 2"
},
]
I want to reformat it to become:
const mydata = [
{
"Title 1": "Value 1"
},
{
"Title 2": [
{
"Name": "Name 1",
"Age": "20"
},
{
"Name": "Name 2",
"Age": "30"
},
]
},
]
I have tried this code to achieve it:
mydata.map((dt: any) => {
dt.title: dt.value
});
However, it seems not working.
Any idea how can I reformat it to the one I desire?
Thanks.
Please use following code.
Reference URL How to use a variable for a key in a JavaScript object literal?
const mydata = [
{
"id": 001,
"value": "Value 1",
"title": "Title 1"
},
{
"id": 002,
"value": [
{
"Name": "Name 1",
"Age": "20"
},
{
"Name": "Name 2",
"Age": "30"
},
],
"title": "Title 2"
},
];
let reData = [];
mydata.forEach((dt)=>{
reData.push({[dt.title]: dt.value});
});
console.log(reData);
If you want to transform the array to a different type of variable, use [reduce][1]
const mydata = [
{
id: 001,
value: "Value 1",
title: "Title 1",
},
{
id: 002,
value: [
{
Name: "Name 1",
Age: "20",
},
{
Name: "Name 2",
Age: "30",
},
],
title: "Title 2",
},
];
const data = mydata.reduce(
(acc, cur) => ({ ...acc, [cur.title]: cur.value }),
{}
);
console.log(data);
Your map function has an error, and your key assignment has another one. Let's fix it.
const newData = mydata.map((dt: any) => ({
[dt.title]: dt.value,
}));
First: You can't return an object from an arrow function without parenthesis, if you don't use it, the code will think it is a function body not an object.
Second: If you want to return a value as a key, you need put it inside "[ ]" (Square brackets)
Just that, simple mistakes, at the end you came up with the right logic to solve it
Add brackets around the return value.
Use square brackets for a computed property name.
const mydata = [
{
"id": 001,
"value": "Value 1",
"title": "Title 1"
},
{
"id": 002,
"value": [
{
"Name": "Name 1",
"Age": "20"
},
{
"Name": "Name 2",
"Age": "30"
},
],
"title": "Title 2"
},
];
const res = mydata.map(({value, title})=>({[title]: value}));
console.log(res);

Merging Same JSON Element

How can you merge this JSON data. Simply merging the same objects and adding the different "product_code" into an array from the same JSON element dependent on the "product_web_name".
{
"search_result": {
"results": [
{
"product_id": "1",
"product_code": "Aa",
"product_description": "test desc a",
"product_web_name": "Product A"
},
{
"product_id": "2",
"product_code": "Bb111",
"product_description": "test desc b",
"product_web_name": "Product B",
},
{
"product_id": "2",
"product_code": "Bb222",
"product_description": "test desc b",
"product_web_name": "Product B"
},
{
"product_id": "2",
"product_code": "Bb333",
"product_description": "test desc b",
"product_web_name": "Product B",
}
]
}
}
The Final output results or the outcome would be something like this
{
"search_result": {
"results": [
{
"product_id": "1",
"product_code": "Aa",
"product_description": "test desc a",
"product_web_name": "Product A"
},
{
"product_id": "2",
"product_code": [
{
"product_code_id":"1",
"product_code": "Bb111"
},
{
"product_code_id":"2",
"product_code": "Bb222"
},
{
"product_code_id":"3",
"product_code": "Bb333"
}
],
"product_description": "test desc b",
"product_web_name": "Product B",
}
]
}
}
I want to merge the JSON data with adding the same element. I have tried the code so far.
var mergethis = {};
data = result.search_result.results.filter(function(entry) {
var previous;
if(mergethis.hasOwnProperty(entry.product_web_name)) {
previous = mergethis[entry.product_web_name];
previous.data.push(entry.data);
return false;
}
if (!Array.isArray(entry.data)) {
entry.data = [entry.data];
}
mergethis[entry.product_web_name] = entry;
return true;
});
console.log(mergethis);
Here's a snippet below to get you started, you can massage the output as needed. All of this came from using Array.prototype.reduce Array.prototype.map and Object.prototype.keys. A utility tool may also help simplify if you look at lodash or underscore
var data = {
"search_result": {
"results": [
{
"product_id": "1",
"product_code": "Aa",
"product_description": "test desc a",
"product_web_name": "Product A"
},
{
"product_id": "2",
"product_code": "Bb111",
"product_description": "test desc b",
"product_web_name": "Product B",
},
{
"product_id": "2",
"product_code": "Bb222",
"product_description": "test desc b",
"product_web_name": "Product B"
},
{
"product_id": "2",
"product_code": "Bb333",
"product_description": "test desc b",
"product_web_name": "Product B",
}
]
}
};
var grouped = data.search_result.results.reduce(function(acc, value) {
var groupArray = acc[value.product_id];
if (!groupArray) {
groupArray = [];
acc[value.product_id] = groupArray;
}
groupArray.push(value);
return acc;
}, {});
//console.log(grouped);
data.search_result.results = Object.keys(grouped).map(function(key) {
//return grouped[key][0];
return {
"product_id": grouped[key][0].product_id,
"product_description": grouped[key][0].product_description,
"product_code": grouped[key].length === 1 ? grouped[key][0].product_code : grouped[key]
};
});
console.log(data);
for(var i = 0 ; i < a.search_result.results.length; i++){
switch(a.search_result.results[i].product_web_name){
case 'Product A':
a.search_result.results[i].product_code = 'something1';
break;
case 'Product B':
a.search_result.results[i].product_code = 'something2';
break;
default :
a.search_result.results[i].product_code = 'something3';
}
}

Javascript Loop and filter out the data thats null or empty

I have some data and I need to filter out the data thats null or empty and create a new data list thats filtered.
In this case sometimes "names" array is null so I need that data out.
{
"people": [
{
"id": "2",
"description": "desc here",
"names": [
{
"name": "name here",
},
{
"name": "name here",
}
],
"other": "0"
},
{
"id": "200",
"description": "desc here",
"names": null
"other": "0"
},
{
"id": "64",
"description": "desc here",
"names": [
{
"name": "name here",
},
{
"name": "name here",
}
],
"other": "1"
}
]
}
How can I do this?
You could iterate the arrays and objects recursive until a primitive is found. Then check and return the value.
function copy(object) {
var o;
if (Array.isArray(object)) {
return object.reduce(function (r, a) {
var v = copy(a);
v.names !== null && v.names !== '' && r.push(v);
return r;
}, []);
}
if (object !== null && typeof object === 'object') {
o = {};
Object.keys(object).forEach(function (k) {
o[k] = copy(object[k]);
});
return o;
}
return object;
}
var data = { people: [{ id: "2", description: "desc here", names: [{ id: "345", name: "name here", }, { id: "54", name: "name here", foo: "", }], other: "0" }, { id: "2", description: "desc here", names: null, other: "0" }, { id: "64", description: "desc here", names: [{ id: "87", name: "name here", }, { id: "53", name: "name here", }], other: "1" }] },
result = copy(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var newArray = oldArray.filter(function(v){return v!==''});
new_array=yourObject.people.filter(function(elem){
return elem.names!==null && elem.names!==""
});

Categories

Resources