Refactor Javascript Object Traversal - javascript

The code that I have does result in the desired output. However, in my opinion, using these nested for loops is a little ugly and I am trying to refactor my work.
My question is: do you have any suggestions on refactoring the code so that I can avoid the need of using these nested for loops?
I want to loop over a nested object and end up with a result of all unique keys, and an array of all values for that unique key.
{"ABC":["1","100","6"],"DEF":["10","2","5"],"GHI":["14","9"],"HGI":["4"]}
const data = {
something: [
{
innerSomething: {
list: [
{
title: "ABC",
amount: "1"
},
{
title: "DEF",
amount: "10"
},
{
title: "GHI",
amount: "14"
}
],
}
},
{
innerSomething: {
list: [
{
title: "ABC",
amount: "100"
},
{
title: "DEF",
amount: "2"
},
{
title: "GHI",
amount: "9"
}
],
}
},
{
innerSomething: {
list: [
{
title: "ABC",
amount: "6"
},
{
title: "DEF",
amount: "5"
},
{
title: "HGI",
amount: "4"
}
],
}
}
]
};
const results = {};
data.something.forEach((item) => {
item.innerSomething.list.forEach((list) => {
if (results[list.title]) {
// exists already, just push the amount
results[list.title].push(list.amount)
} else {
// Is unique so far, add it to the object
results[list.title] = [list.amount];
}
})
});
console.log(`results: ${JSON.stringify(results)}`);
// These results are the correct and desired output
// {"ABC":["1","100","6"],"DEF":["10","2","5"],"GHI":["14","9"],"HGI":["4"]}

Your implementation is ok, tbh.
The only thing I can suggest differently is to make use of the neat destructuring patterns and spread syntax, considering that the structure of your input object is very well known:
data.something.forEach(({ innerSomething: { list } }) =>
list.forEach(({ title, amount }) =>
results[title] = [...results[title] || [], amount]))

For what it's worth, here is how I'd write it.
const data = {
something: [
{
innerSomething: {
list: [
{
title: "ABC",
amount: "1"
},
{
title: "DEF",
amount: "10"
},
{
title: "GHI",
amount: "14"
}
],
}
},
{
innerSomething: {
list: [
{
title: "ABC",
amount: "100"
},
{
title: "DEF",
amount: "2"
},
{
title: "GHI",
amount: "9"
}
],
}
},
{
innerSomething: {
list: [
{
title: "ABC",
amount: "6"
},
{
title: "DEF",
amount: "5"
},
{
title: "GHI",
amount: "4"
}
],
}
}
]
};
const results = {};
for (let something of data.something) {
for (let item of something.innerSomething.list) {
const { title, amount } = item;
results[title] = results[title] || [];
results[title].push(amount);
}
}
console.log(`results: ${JSON.stringify(results)}`);

Related

How to get all possible ways of passing the quiz

The quiz is implemented in such a way that questions are displayed depending on the user's choice. I need to implement the testing functionality of the questionnaire. After passing in the console should have the same structure as I have provided below. Now my script produces a slightly different structure, not the same as in the example. Example of my code:
const questions = [
{
id: "1",
question: "q1",
answer_1: {
text: "a1",
next_question: "2",
},
answer_2: {
text: "a2",
next_question: "3",
},
},
{
id: "2",
question: "q2",
answer_1: {
text: "a1/a2",
next_question: "",
},
},
{
id: "3",
question: "q3",
answer_1: {
text: "a1",
next_question: "",
},
answer_2: {
text: "a2",
next_question: "4",
},
},
{
id: "4",
question: "q4",
answer_1: {
text: "a1/a2",
next_question: "",
},
},
];
const newQuestionObj = {};
const getAllPath = (arr) => {
const treeStructure = arr.forEach((item) => {
newQuestionObj[item.id] = {
...item,
children: [
...Object.keys(item)
.filter((k) => k.includes("answer"))
.map((k) => ({ ...item[k] })),
],
};
});
const createBranches = ({ question, children }) =>
children.map(({ text, next_question }) =>
next_question.length === 0
? { [question]: text }
: createBranches(newQuestionObj[next_question]).map((obj) => ({
[question]: text,
...obj,
}))
);
return createBranches(newQuestionObj[arr[0].id]);
};
let result = {
path: {
list: getAllPath(questions),
},
};
console.log(result);
Desired result to get:
{
"paths":{
"list":[
[
{
"q1":"a1"
},
{
"q2":"a1/a2"
}
],
[
{
"q1":"a2"
},
{
"q3":"a1"
}
],
[
{
"q1":"a2"
},
{
"q3":"a2"
},
{
"q4":"a1/a2"
}
]
]
}
}

filter object value using node js

Is there any way i can filter values which are present inside Object
[
{
id: "1",
name:"animal_image.jpg"
},
{
id: "2",
name:"fish_image.jpg"
},
{
id: "3",
name:"animal_doc.txt"
},{
id: "4",
name:"fish_doc.txt"
},
{
id: "4",
name:"flower_petals.jpg"
},
{
id: "5",
name:"plant_roots.jpg"
},
{
id: "6",
name:"human_image.jpg"
},
]
i want to filter all the name which contain_image.jpg so output look like this
output=
[ "human_image.jpg",
"anima_image.jpg",
"fish_image.jpg"
]
In this code snippet filtredData is an array of objects where the name includes _image.jpg and output is just an array of names containing _image.jpg
const data = [
{
id: "1",
name: "animal_image.jpg"
},
{
id: "2",
name: "fish_image.jpg"
},
{
id: "3",
name: "animal_doc.txt"
}, {
id: "4",
name: "fish_doc.txt"
},
{
id: "4",
name: "flower_petals.jpg"
},
{
id: "5",
name: "plant_roots.jpg"
},
{
id: "6",
name: "human_image.jpg"
},
]
const filtredData = data.filter(el => el.name.includes("_image.jpg"));
console.log(filtredData);
const output = filtredData.map(el => el.name);
console.log(output);
filter & map
const output = arr
.filter(x => x.name.endsWith('_image.jpg'))
.map(x => x.name);

filtered by name using node js

Is there any way i can filter files with given extension and then further filter them
for eg: I have .txt extension and i want to get all my .txt from an array
file=
[ "animal_bio.txt",
"xray.pdf",
"fish_bio.txt",
"mammal_doc.txt",
"human_bio.txt",
"machine.jpg"
]
filtered output contain all .txt extension and further it should contain all the files which have _bio.txt name in it.
so output look like
futherFile=
[ "human_bio.txt",
"fish_bio.txt",
"animal_bio.txt"
]
You can use String.protytype.endsWith function to compare the strings with your extension
const file =
[ "animal_bio.txt",
"xray.pdf",
"fish_bio.txt",
"mammal_doc.txt",
"human_bio.txt",
"machine.jpg"
]
result = file.filter((fileName) => fileName.endsWith("_bio.txt"));
console.log(result)
You can use the Array.filter method and use the String.endsWith method to filter. An example -
// List of files
file = ["animal_bio.txt",
"xray.pdf",
"fish_bio.txt",
"mammal_doc.txt",
"human_bio.txt",
"machine.jpg"
]
// Filtering by extension
file.filter(x => x.endsWith(".txt"));
Hope it helped :)
You can easily achieve this result using reduce and match
When matching for the doc or bio, You can even restrict more to get the string only if _doc.txt is at end of the string using Regular expression /_bio.txt$/
const arr = [
{
id: "1",
name: "animal_bio.txt",
},
{
id: "2",
name: "xray.pdf",
},
{
id: "3",
name: "animal_doc.txt",
},
{
id: "4",
name: "fish_doc.txt",
},
{
id: "5",
name: "flower_petals.jpg",
},
{
id: "5",
name: "plant_roots.jpg",
},
{
id: "6",
name: "human_image.jpg",
},
{
id: "7",
name: "human_bio.txt",
},
{
id: "8",
name: "mammal_doc.txt",
},
];
const result = arr.reduce((acc, { name }) => {
if (name.match(/\.txt$/)) {
if (name.match(/_bio/)) {
acc[0].push(name);
} else {
acc[1].push(name);
}
}
return acc;
},
[[], []]
);
console.log(result);
Then you can get the element containing doc and bio using array destructuring as
const [bioArr, docArr] = result;
console.log(bioArr);
console.log(docArr);
const arr = [
{
id: "1",
name: "animal_bio.txt",
},
{
id: "2",
name: "xray.pdf",
},
{
id: "3",
name: "animal_doc.txt",
},
{
id: "4",
name: "fish_doc.txt",
},
{
id: "5",
name: "flower_petals.jpg",
},
{
id: "5",
name: "plant_roots.jpg",
},
{
id: "6",
name: "human_image.jpg",
},
{
id: "7",
name: "human_bio.txt",
},
{
id: "8",
name: "mammal_doc.txt",
},
];
const result = arr.reduce(
(acc, { name }) => {
if (name.match(/\.txt$/)) {
if (name.match(/_bio/)) {
acc[0].push(name);
} else {
acc[1].push(name);
}
}
return acc;
},
[[], []]
);
const [bioArr, docArr] = result;
console.log(bioArr);
console.log(docArr);
you can use filter function from ES6 like:
const txtFile = file.filter((item) => (item.split('_'))[1] === 'bio.txt')

Javascript Object Tranformation

I am using the map to transforming the JSON. Instead of Single object, I am getting an Array of data.
Snipt Shown Below.
I am trying to achieve following JSON
{
"data": {
"test1": {
"abc": {
"size": "big",
"id": "1"
}
},
"test2": {
"abc": {
"size": "small",
"id": "2"
}
}
}
}
But getting following JSON
{
"data": [{
"test1": {
"abc": {
"size": "big",
"id": "1"
}
}
}, {
"test2": {
"abc": {
"size": "big",
"id": "2"
}
}
}]
}
Here is my code.
const test = [{ id: 'ddec9c7f-95aa-4d07-a45a-dcdea96309a9',
ad_id: 'test1',
country: 'ID',
abc: { size: 'big', id: '1' },
},
{ id: 'ddec9c7f-95aa-4d07-a45a-dcdea96309a9',
ad_id: 'test2',
country: 'ID',
abc: { size: 'small', id: '2' },
},
];
const transformedTest = test.map(result =>
({ [result.ad_id]: { abc: result.abc } }));
const data = { data: transformedTest };
console.log(JSON.stringify(data));
Any help will be appreciated
Try this:
const test = [
{
"id": "ddec9c7f-95aa-4d07-a45a-dcdea96309a9",
"ad_id": "test1",
"country": "ID",
"abc": {
"size": "big",
"id": "1"
}
},
{
"id": "ddec9c7f-95aa-4d07-a45a-dcdea96309a9",
"ad_id": "test2",
"country": "ID",
"abc": {
"size": "big",
"id": "2"
}
}
];
const result = test.reduce((acc,{ad_id, abc})=> (acc.data[ad_id] = {abc}, acc),{data:{}})
console.log(result);
const transformedTest = test.reduce((pv, result) =>
({...pv, [result.ad_id]: { abc: result.abc } }), {});
You could take Object.fromEntries and map new key/value pairs and get a new object from it.
const
test = [{ id: 'ddec9c7f-95aa-4d07-a45a-dcdea96309a9', ad_id: 'test1', country: 'ID', abc: { size: 'big', id: '1' } }, { id: 'ddec9c7f-95aa-4d07-a45a-dcdea96309a9', ad_id: 'test2', country: 'ID', abc: { size: 'small', id: '2' } }];
transformedTest = Object.fromEntries(test.map(({ ad_id, abc} ) => [ad_id, { abc }]));
data = { data: transformedTest };
console.log(data);
Loop through the array using a simple for...of loop and create a data object. Use Shorthand property names to create the abc nesting:
const test=[{id:'ddec9c7f-95aa-4d07-a45a-dcdea96309a9',ad_id:'test1',country:'ID',abc:{size:'big',id:'1'},},{id:'ddec9c7f-95aa-4d07-a45a-dcdea96309a9',ad_id:'test2',country:'ID',abc:{size:'small',id:'2'}}]
const data = {}
for(const { ad_id, abc } of test) {
data[ad_id] = { abc }
}
console.log({ data })
.as-console-wrapper { min-height: 100%; }
The function map returns an array rather than a specific key-value object, an alternative is using the function reduce to build the desired output.
const test = [{ id: 'ddec9c7f-95aa-4d07-a45a-dcdea96309a9', ad_id: 'test1', country: 'ID', abc: { size: 'big', id: '1' },},{ id: 'ddec9c7f-95aa-4d07-a45a-dcdea96309a9', ad_id: 'test2', country: 'ID', abc: { size: 'big', id: '2' },}],
data = { data: test.reduce((a, result) =>
({...a, [result.ad_id]: { abc: result.abc } }), Object.create(null))};
console.log(JSON.stringify(data, null, 2));
.as-console-wrapper { min-height: 100%; }

Setting array keys dynamically based on length

Given an array in this format:
[
[{
name: "name",
value: "My-name"
},
{
name: "qty",
value: "1"
},
{
name: "url",
value: "test.com"
},
{
name: "comment",
value: "my-comment"
}
],
[{
name: "name",
value: "My-name2"
},
{
name: "qty",
value: "3"
},
{
name: "url",
value: "test2.com"
}
],
[{
name: "name",
value: "My-name3"
},
{
name: "qty",
value: "1"
},
{
name: "url",
value: "test3.com"
},
{
name: "comment",
value: "my-comment3"
}
]
]
I'm looking to switch that to:
[
[
{ name: "My-name" },
{ qty: "1" },
{ url: "test.com" },
{ comment: "my-comment", }
],[
{ name: "My-name2" },
{ qty: "3" },
{ url: "test2.com",
],[
{ name: "My-name3", },
{ qty: "1", },
{ url: "test3.com", },
{ comment: "my-comment3", }
]
]
In other words, swapping out the array keys but maintaining the object structure within each array element.
I've tried looping over each element and can swap the keys out using something like:
newArray[iCount][item.name] = item.value;
However I'm then struggling to preserve the object order. Note that the comment field may or may not appear in the object.
With Array.map() function:
var arr = [
[{name:"name",value:"My-name"},{name:"qty",value:"1"},{name:"url",value:"test.com"},{name:"comment",value:"my-comment"}],
[{name:"name",value:"My-name2"},{name:"qty",value:"3"},{name:"url",value:"test2.com"}],
[{name:"name",value:"My-name3"},{name:"qty",value:"1"},{name:"url",value:"test3.com"},{name:"comment",value:"my-comment3"}]
],
result = arr.map(function(a){
return a.map(function(obj){
var o = {};
o[obj.name] = obj.value
return o;
});
});
console.log(result);
Check my moreBetterOutput value. I think will be better.
If you still need a result like your example in the question then you can check output value.
const input = [
[
{
name:"name",
value:"My-name"
},
{
name:"qty",
value:"1"
},
{
name:"url",
value:"test.com"
},
{
name:"comment",
value:"my-comment"
}
],
[
{
name:"name",
value:"My-name2"
},
{
name:"qty",
value:"3"
},
{
name:"url",
value:"test2.com"
}
],
[
{
name:"name",
value:"My-name3"
},
{
name:"qty",
value:"1"
},
{
name:"url",
value:"test3.com"
},
{
name:"comment",
value:"my-comment3"
}
]
]
const output = input.map(arr => arr.map(obj => ({[obj.name]: obj.value})))
const moreBetterOutput = output.map(arr => arr.reduce((acc, item, index) => {
acc[Object.keys(item)[0]] = item[Object.keys(item)[0]];
return acc;
}, {}) )
//console.log(output);
console.log(moreBetterOutput);
Another map function:
const result = array.map( subarray =>
Object.assign(...subarray.map( ({name, value}) => ({ [name] : value }) ))
);

Categories

Resources