How to map JSON Object to array with keys in javascript - javascript

There's about million questions (and answers) out there on this topic, but none of them are doing what I need to do. I have a JSON object where the value for each key is an object. I want to convert this to an array and maintain the top level keys.
{
"someKey1": {
active: true,
name: "foo"
},
"someKey2": {
active: false,
name: "bar"
}
}
If I use Object.keys() I get the top level keys, but not their values. If I use Object.values() I get an array with all the values, but not their keys. I'm trying to use keys and map, but am only getting the values returned:
const data = {
"someKey1": {
active: true,
name: "foo"
},
"someKey2": {
active: false,
name: "bar"
}
}
const items = Object.keys(data).map(function(key) {
return data[key];
});
// returns [{active: true, name: foo},{active: false, name: bar}]
Is there a way to get both? I want to get an array I can iterate over that looks something like this:
[{
key: someKey1,
active: true,
name: "foo"
},
{
key: someKey2,
active: true,
name: "foo"
}]
OR
[
"someKey1": {
active: true,
name: "foo"
},
"someKey2": {
active: false,
name: "bar"
}
]

I think you are going in the right direction, if you want to add the "key" property you have to map the properties manually and for the second option since you don't need the "key" property it can be done a little bit more elegantly:
For the first option:
Object.keys(data).map(v => ({
key: v,
...data[v]
}));
For the second option even simpler:
Object.keys(data).map(v => ({[v]: {...data[v]}}))

You can easily map your data to a new object:
Object.keys(data).map(key => ({ ...data[key], "key": key }));

Related

how to add element to object in array javascript

I have an array of objects and it is structured as so:
let array1 = [{}]
array1.push({
title: 'myTitle1',
extendedProps: {
field1: 'hello'
}
})
now what I am trying is do an axios.get request and use the data returned to add a new element into array1. I am successfully retrieving the data from the axios request, however, when I try to push the data: res.data[i]._doc.userIsReg, like so:
array1.push({
...array1,
extendedProps: {
...array1.extendedProps,
userIsReg: true
}
})
as you can see, I am using the spread functionaluty to include the current data from array1 into the array and then I try to append a new element userIsReg to the object extendedProps. Now, I assumed this would work, however, when I do this, it creates new object entries within the array and includes everything from inside the array currently (from spread functionality) and adds new entries with the userIsReg in there.
so to be more clear, I start with this:
[{
title: 'myTitle1',
extendedProps: {
field1: 'hello'
}
},
{
title: 'myTitle2',
extendedProps: {
field1: 'hello2'
}
},
]
and then once i do the array1.push with the spread functionality, i get this:
[{
title: 'myTitle1',
extendedProps: {
field1: 'hello'
}
},
{
title: 'myTitle2',
extendedProps: {
field1: 'hello2'
}
},
{
title: 'myTitle1',
extendedProps: {
field1: 'hello',
userIsReg: true
}
},
{
title: 'myTitle2',
extendedProps: {
field1: 'hello2',
userIsReg: true
}
},
]
so basically doubles everything up, instead of appending it to the current array. how would i fix this?
You can do something like following once you have response
array1.map(element => ({...element, userIsReg: true}))
It will add userIsReg flag to each object of your array.
Now as you want it inside extendedProps, you can use following
array1.map(element => (
{...element,
extendedProps: {
...element.extendedProps,
userIsReg: true
}}))
as you defined your array in let type you can do it in this way:
array1 = [...array1.filter(item=> item.title === selected_item.title ),{
...selected_item,userIsReg: true
}]
what I did is to just remove the previous element and add a new one with a new value
if you want to preserve order of array you can sort that
Try this way :
let array1 = [{
title: 'myTitle1',
extendedProps: {
field1: 'hello'
}
},
{
title: 'myTitle2',
extendedProps: {
field1: 'hello2'
}
}];
array1.forEach(obj => {
obj.extendedProps['userIsReg'] = true // this value is coming from API
});
console.log(array1);

How to create nested array of objects using useState react hook

I have a problem finding a solution on how to fill empty nested array by using the useState hook. I'm a react beginner and I have might miss something. In steps below I shall try to summarize the problem.
1.) I receive this data format as props - {question}:
{
category: "General Knowledge"
correct_answer: "Arby's"
difficulty: "easy"
incorrect_answers: (3) ['McDonald's', 'Burger King', 'Wendy's']
question: "In which fast food chain can you order a Jamocha Shake?"
type: "multiple"
}
What my goal output is ty create an object with this structure:
value: "Opel",
isClicked: false,
isCorrect: true,
incorrect_answers: [
{isClicked: false, value: "Bugatti"},
{isClicked: false, value: "Bentley},
{etc...}
]
With this approach I achieve the result, but I would like to find a more correct react way.
useEffect(() => {
const obj = {
value: question.correct_answer,
isClicked: false,
isCorrect: true,
incorrect_answers: []
}
question.incorrect_answers.map((item) =>
obj.incorrect_answers.push({
value: item,
isClicked: false,
})
)
setAnswers(obj)
}, [])
My goal is to have mentioned data structure formed in useState with the right approach on how to access nested arr and fill it with objects.
a) I use the useState for setting up state and its data structure for answers obj.
const [answers, setAnswers] = useState({
value: question.correct_answer,
isClicked: false,
isCorrect: true,
incorrect_answers: [
//I want multiple objects {value: '...', isClicked: ...},
// Would be nice if I was able to insert objects in this a) step.
]
})
b) Perhaps on the useEffect or on some other funcion I want to fill incorect_answers array with objects.
useEffect(() => {
// c) Im accesing the answers state and filling incorect_answers with obj
setAnswers(prevState => {
return {
...prevState,
incorrect_answers: [{
value: question.incorrect_answers.map((item) => item),
isClicked: false,
isCorrect: false
}]
}
})
// d) my output:
/* {
value: "Opel",
isClicked: false,
isCorrect: true,
incorrect_answers: [
{isClicked: false, value: [bugatti, bentley, bmw, citroen]},
]
} */
}, [])
If you're using map you shouldnt ignore the response from it, and you don't need to push to the array
useEffect(() => {
const obj = {
value: question.correct_answer,
isClicked: false,
isCorrect: true,
incorrect_answers: question.incorrect_answers.map(item => ({
value: item,
isClicked: false
}))
}
setAnswers(obj)
}, [])
The same method can be used when first filling your state
const [answers, setAnswers] = useState({
value: question.correct_answer,
isClicked: false,
isCorrect: true,
incorrect_answers: question.incorrect_answers.map(item => ({
value: item,
isClicked: false
}))
})

How do I preserve the order of this javascript array sort?

const modules = [
{ name: 'Wood', checked: false },
{ name: 'Metal', checked: false },
{ name: 'Earth', checked: true },
{ name: 'Water', checked: false },
{ name: 'Air', checked: true },
{ name: 'Fire', checked: false },
]
I am trying to sort the array so that the True values come first , then False values.
const orderedModules = modules.sort((a, b) => (a.checked ? -1 : 1))
However, I'd like to preserve the order of the True values. The code above sometimes puts Air first, then Earth (if ran twice). How can I preserve the order all the time?
The callback used as the compare function can return 3 options: negative number, positive number or zero. The negative number indicates that the first parameter should be before the second parameter in the array order. The positive number indicates that the first parameter should be after the second parameter in the array order. And zero means that the order should be kept as is.
Sort array method on MDN
If you want to order just the true values first in the same order in the array and then the false values, probably adding more logic to return zero from the compare function if both are true will solve your issue.
Here is an example:
const modules = [
{ name: 'Wood', checked: false },
{ name: 'Metal', checked: false },
{ name: 'Earth', checked: true },
{ name: 'Water', checked: false },
{ name: 'Air', checked: true },
{ name: 'Fire', checked: false },
];
modules.sort((a,b) => a.checked && b.checked ? 0 : a.checked ? -1 : 1);
console.log(modules);
If you don't mind creating a new array, just iterate over the array twice. The first time, push to the new array the objects with the true values as the iteration encounters them. The second time, push the objects with the false values. (JavaScript passes objects by reference so the new array won't cause them to get duplicated.)
Probably this might help you. not sure for optimize way but this function iterate over an array one time only.
I am using reduce function to separate out true and false values and then return them in the order you want.
const shortItems = (array) => {
const orderedModulesObject = array.reduce((orderedModulesObject, currentModule) => {
if(currentModule.checked){
orderedModulesObject.trueValues = orderedModulesObject.trueValues.concat(currentModule);
} else {
orderedModulesObject.falseValues = orderedModulesObject.falseValues.concat(currentModule);
}
return orderedModulesObject;
}, { trueValues: [], falseValues: []});
return orderedModulesObject.trueValues.concat(orderedModulesObject.falseValues);
}
const modules = [
{ name: 'Wood', checked: false },
{ name: 'Metal', checked: false },
{ name: 'Earth', checked: true },
{ name: 'Water', checked: false },
{ name: 'Air', checked: true },
{ name: 'Fire', checked: false },
]
console.log(shortItems(modules));
the reason is that sort actually changes the original array. Although modules is defined with const, the values inside can change, as long as you don't assign the variable to something else.
according to this answer, you can sort without mutating the original using spread syntax. this code should work:
const orderedModules = [...modules].sort((a, b) => (a.checked ? -1 : 1))
to make an array or object not able to be modified, you can use Object.freeze()
const modules = Object.freeze([
{ name: 'Wood', checked: false },
{ name: 'Metal', checked: false },
{ name: 'Earth', checked: true },
{ name: 'Water', checked: false },
{ name: 'Air', checked: true },
{ name: 'Fire', checked: false },
])
Edit: I just realized the order isn't correct, but it at least is the same every time. but that's because the sorting isn't exactly right. here's the correct code:
const orderedModules = [...modules].sort((a, b) => (a.checked != b.checked ? (a.checked ? -1 : 1 ) : 0))

Format a nested object

I have a javascript variable with the following structure
var recurse = {
level_0: [
{
level_1 : [
{
level_2_0 : [
{
level_3_0: {
valid: true,
available: false
}
}, {
level_3_1 : {
valid: true,
available: true
}
}]
}, {
level_2_1 : [
{
level_3_0: {
valid: true,
available: false
}
}, {
level_3_1 : {
valid: true,
available: true
}
}]
}]
}]
}
Final required output structure
var recurse = {
name: "level_0",
property: [
{
name: "level_1",
property: [
{
name: "level_2_0",
property: [
{
name: "level_3_0",
property: {
valid: true,
available: false
}
}, {
name: "level_3_1",
property: {
valid: true,
available: true
}
}]
}, {
name: "level_2_1",
property: [
{
name: "level_3_0",
property: {
valid: true,
available: false
}
}, {
name: "level_3_1",
property: {
valid: true,
available: true
}
}]
}]
}]
}
Facing problem with cloning and updating the structure for this nested object using generic methods.
How can I achieve the required final object structure using simple javascript or reactjs properties.
Which is the most appropriate method to clone a javascript object?
Note: the object names- level_0, level_1 level_2_0 could be random or dynamic.
You can write a simple recursive function that recurses on the item if the item value is an array like below
var recurse = {
level_0: [
{
level_1 : [
{
level_2_0 : [
{
level_3_0: {
valid: true,
available: false
}
}, {
level_3_1 : {
valid: true,
available: true
}
}]
}, {
level_2_1 : [
{
level_3_0: {
valid: true,
available: false
}
}, {
level_3_1 : {
valid: true,
available: true
}
}]
}]
}]
}
function format(data) {
return Object.entries(data).map(([key, value]) => {
if(Array.isArray(value))
return {
name: key,
property: [].concat(...value.map(item => format(item)))
}
return {
name: key,
property: value
}
})
}
console.log(format(recurse));
You can transform it with something like this:
const transform = (tree) =>
Object (tree) === tree
? Object .entries (tree) .map (([name, properties]) => ({
name,
properties: Array .isArray (properties) ? properties .map (transform) : properties
})) [0] // This is a strage thing to do with an object! The original data structure is odd.
: tree
var recurse = {level_0: [{level_1: [{level_2_0: [{level_3_0: {valid: true, available: false}}, {level_3_1: {valid: true, available: true}}]}, {level_2_1: [{level_3_0: {valid: true, available: false}}, {level_3_1: {valid: true, available: true}}]}]}]}
console .log (transform (recurse))
.as-console-wrapper {min-height: 100% !important; top: 0}
But the data structure you start with is quite odd. Each level of nesting except the innermost can only have a single property. Is that really how your data comes to you? It's a strange use of objects. (Then again, you are asking about how to reforat it...)
The [0] on the sixth line of the above deals with that oddity of the input.

Make a new object from an array of objects [duplicate]

This question already has answers here:
Create an object with dynamic property names [duplicate]
(2 answers)
Closed 6 years ago.
This is possibly a duplicate, but everywhere I search I can only seem to find people wanting to create an array of objects.
Basically I'm trying to achieve the opposite, pull certain values of an array of objects out into an object. It's twisting my head a little so any of you JS gurus out there if you could give me a hand it would be very very appreciated!
Basically I have an array of objects like this:
[
{ field: 'name', value: 'sam', isRequired: true },
{ field: 'email', value: 'sam#dummyemail.net', isRequired: true },
{ field: 'message', value: 'hey', isRequired: false },
]
They're split up this way because I go through the fields for validation.
After the validation phase I want to map the field and value properties to name value pairs within a new object e.g:
{
name: 'sam',
email: 'sam#dummyemail.net',
message: 'hey',
}
Like I said any help would be amazing! Cheers.
Array#map each object in the array into a new object with field as the key of a property, and value, well, the value of the property. Then combine the new objects array to object using the spread syntax and Object#assign:
const arr = [
{ field: 'name', value: 'sam', isRequired: true },
{ field: 'email', value: 'sam#dummyemail.net', isRequired: true },
{ field: 'message', value: 'hey', isRequired: false },
];
const result = Object.assign({}, ...arr.map(({ field, value }) => ({ [field]: value })));
console.log(result);
you could map array into object like this
var array = [
{ field: 'name', value: 'sam', isRequired: true },
{ field: 'email', value: 'sam#dummyemail.net', isRequired: true },
{ field: 'message', value: 'hey', isRequired: false },
];
var object = {};
array.forEach(x => {
object[x.field] = x.value;
});
console.log(object);

Categories