Typescript - generate TS object from array - arrays

My problem is that i get an information from server - array of objects. And i would like to "push" every object from array to new object so the inupt looks:
[{key : value}],[{key2:value2}]....
And the output should look like:
{key:value,
key2: value2 ... }
I don't know the syntax of "push" to make new object look like my desire output.

Here's a fun little trick:
const input = [{key: 'value'}, {key2: 'value2'}];
// Object.assign() always returns any with rest arguments,
// you should cast to the actual type you're expecting here.
const result = Object.assign({}, ...input) as MyApiType;
console.log(result);
How does this work?
Object.assign() will take an arbitrary number of arguments, and merges them.
Object.assign({}, {hello: 'world'}, {goodbye: 'world'});
// {hello: 'world', goodbye: 'world'}
We pass an empty object as the first parameter and spread input into the rest of the parameters.
Why the {}, ...input? Why not just ...input?
Because Object.assign() is destructive, it mutates the first argument passed to it. If I were to call Object.assign(...input), input[0] would have been changed. We want to avoid changing the input, because it's usually unexpected behavior to those looking at the code from the outside.

What you are looking for is not push put Array.reduce (plain old js). It allows you to reduce the array to a single object. You can use it this way.
let data: [{ key: string, value: string }] = [{
key: '1',
value: '2'
}];
let obj = data.reduce((prev, current) => {
prev[current.key] = current.value;
return prev;
}, {});

Related

Why is Array.push() throwing an error but Array.concat() does not?

I am trying to add an array to an existing array. The initial data is like this:
props: {
ItemData: // This prop gets populated with data from the parent component. That data comes from a Vuex store.
{
type: Array,
default: null
}
}
setup(props) {
const Item = ref(props.ItemData);
onMounted( ()=> {
Form.Title = Item[0].Title
Form.Files = Item[0].Files
}
)
}
Which results in something like this:
Form = reactive({
Title: null,
Files: [
{name: "photo", extension: ".webp" size: '1024'},
{name: "doc", extension: ".pdf" size: '2048'}
]
)}
The data in Form.Files comes from a prop that is sent to this component from Vuex data:
Form.Files may also be an empty array [ ] if there are no existing files when the form is loaded.
When the user adds new files, the data for the new files is like this:
NewFiles = [
[
File,
{name: "newphoto", extension: ".jpg", size: "1024" }
],
[
File,
{name: "newdoc", extension: ".docx", size: "2048" }
]
]
File is the file object itself as selected via an input field e.g. <input multiple type="file" />
So how do we add NewFiles to Form.Files? I tried the following:
Form.Files.push(...NewFiles);
That somewhat works in that the Vue debugger shows the NewFiles array added to Form.Files, but it thrown an error of [vuex] do not mutate vuex store state outside mutation handlers. and the rest of the code fails.
So I tried concat() which works:
const AllFiles = Form.Files.concat(NewFiles);
Form.BroadcastFiles = AllFiles;
There's no errors but I don't understand why push() would cause a Vuex error when I'm not touching any Vuex state, and even if I was, why doesn't the contact() cause the same error?
.concat returns a new array with both the current items and the new ones where .push modifies the current array instead of returning a new one.
If that array was initially loaded from vuex it results in an unwanted mutation of the store.
Edit:
In the store
mutations: {
addFile(state, file) {
state.ItemData[0].Files.push(file);
},
}
In the component
NewFiles.forEach(file => store.commit("addFile", file));
I suggest you check this article about the use of spread operator in Javascript, and array reference How to Use the Spread Operator (…) in JavaScript especially the "A note about copying by reference" section. So basically you are mutating the state of Form.Files while using concat and you are losing the reference using the spread operator. One solution can be:
let AllFiles = [...Form.Files];
AllFiles.concat(NewFiles);
Form.BroadcastFiles = AllFiles;

Return array when use .reduce Angular

The following function does not return an array but only an object element. I do not understand why
const selectedId = selectedItems.reduce<string[]>(
(acc, curr, index) => ({
...acc,
item: curr.id
}),
[]
);
console.log('selectedId ', selectedId ); // {"item": "1"}
I want to select an array like this: item: ["1", "2", "3"]
Please any idea, who is a problem in my code?
It returns an object because it returns whatever the last thing your callback function returned was,¹ and your callback function is returning an object (not an array).
You may have meant to use [] rather than {} for the callback's return value (=> [...acc, item: curr.id]), but if so, that operation is more clearly expressed via map:
const selectedId = selectedItems.map(item => item.id);
(I'd also suggest using the plural — selectedIds — since it holds multiple ID values.)
¹ If you use reduce on an empty array, your callback is never called, and so the seed value you provided is returned instead. (And if you don't provide one, reduce throws an error.)

Typescript Object.keys() always returns an empty array

First of all, I would like to mention that although this question has been asked several times before, none of the solutions seem to work for me.
Basically, I am using Ionic React, and at one point in my code, I have an Object which I declare as:
const [allArrays, setAllArrays] = useState<AllArraysObject>({});
AllArraysObject is a custom type which is declared in another file as:
export interface myType{
a: number,
b: string,
...
}
export type AllArraysObject= {
[key: string]: myType[]
}
What I actually expect to create is an object which looks like this:
{
"1": [
{a: 10, b: "Hello", ...},
{a: 11, b: "Hi", ...},
...
],
...
}
which is essentially an Object of Arrays of Objects. The Issue is that I cannot use Object.keys(AllArrays) to get an array of all the keys of the object (like ["1", "2", ...]). I have used the same method previously in the application when it worked perfectly fine (at that point, I had an Object of objects).
I have tried the following:
Object.keys(allArrays)
Object.keys(allArrays as Object)
Object.keys(JSON.parse(JSON.stringify(allArrays)))
Object.getOwnPropertyNames(allArrays)
each of which returns me an empty Array(0). As expected, if I console.log(allArrays), I can see all the properties of the object, and on running the above-mentioned functions in the console, I do get the desired output.
I wish to know what mistake I am committing here (which could possibly be because of a gap in my understanding of how Objects work) and I also seek an explanation of the above behavior. Any help would be deeply appreciated.
Remember that useState has an initial value, in this case it's an empty object {}. I would assume you are logging the values before they are set.
const Component = () => {
const [allArrays, setAllArrays] = useState<AllArraysObject>({});
useEffect(() => {
const sampleObject = { "1": [ { a: 10, b: "Hello" }, { a: 11, b: "Hi" } ] }
setAllArrays(sampleObject);
}, [])
console.log(Object.keys(allArrays))
// This be Array(0) on mount until setAllArrays is set
// when useEffect issues a side-effect
// so make a guard if object is empty
if (Object.keys(allArrays).length === 0 && obj.constructor === Object) {
// null else set to anything you desire
return null;
}
return (
<div>{/* use allArrays */}</div>
)
}

How to update nested object in redux

I have an object:
{ //birthdaysObject
'2000':{
'January':
[{
name: 'Jerry'
},
{
name: 'Chris'
}]
},
'2001':{
'February':
[{
name: 'John'
}]
}
When I go to update the redux store it is replacing the entire year (eg. '2000') object with the new one that I send to my reducer.
How can I push the the nested array of objects without replacing the entire year object?
My reducer currently looks like:
return Object.assign({}, state, {
...state,
birthdays: Object.assign({}, state.birthdays, {
...state.birthdays,
...birthdays
})
});
Where ...birthdays would be another object in the same format as the first code snippet.
I am also open to suggestions about the structure of my data, normalizing, etc.
Any help would be appreciated.
The object keys in the birthdaysObject are unknown and are assigned when iterating through a separate object. I've tried kolodny/immutability-helper however the $merge function is returning the same results as what my reducer is already doing.
I had the same problem some time ago.
Follow the way I done it.
You have an object, but I think you should have an array of objects.
I also have different names on variables, but this should not be a problem to understand the logic
//do a copy of the array first
let newSubscriptions = state.customer.master.subscriptions.slice();
//for the value you want to change, find it's position in the array first
const indexInSubscriptions = newSubscriptions.map(function(item) {
return item.id;
}).indexOf( action.id);
//get the child you want to edit and keep it in a new variable
const under_edit_subscription = state.customer.master.subscriptions[indexInSubscriptions];
//go again over the array and where is the value at the index find above, replace the value
newSubscriptions = newSubscriptions.map((item, i) =>
i === indexInSubscriptions ? under_edit_subscription : item
)
//add the whole array into the state
return {
...state,
customer: {
...state.customer,
master: {
...state.customer.master,
subscriptions : newSubscriptions
}
}
}

Async array without using anything async?

It drives me crazy. I have a really simple "problem" and it took me hours and still have no idea whats going on.
I have a child service which inherits from a parent service (I'm using ES6). The constructor takes an 1 argument called options. options will be assigned to this._defaults.
Now before I pass the options into my object (new Service(options)) I populate options with some data. To keep it simple, my current options object looks like this:
const options = {
types: []
}
Now I add some stuff into the types array, like this:
const Types = {
standard: {
some: 'data'
},
freeroll: {
some: 'data'
},
mainevent: {
some: 'data'
},
qualifier: {
some: 'data'
}
};
angular.forEach(Types, (val, key) => {
options.types[key] = true;
});
I assign my service to the scope like this:
$scope.service = new Service(options)
and output the service using console. The console now says the value of _defaults.types is Array(0). When I click on the array the correct values will be shown but the scope is not aware of that.
How is that? Doesn't Array(0) mean that at the time of the console.log() the array wasn't filled with any values but has been later? Like an async function would do?
Here is a plunk of my problem.
The problem is that types is an array and you're treating it like a plain Object. You can solve this one of two ways.
First just change types to an Object:
const options = {
types: {}
};
Or, if you need an Array, change how you're adding items to the array:
angular.forEach(Types, (val, key) => {
options.types.push({
type: key,
value: val
});
});
Note that this is just one way of turning the object into an array, the data structure you end up with is up to you.

Resources