TanStack Table 8 : access nested array in data model - reactjs

I'm looking for a way to access a nested array in my data structure.
My objects in my data array look like this :
{
name: name,
logo: url,
categories: [
{
name: Entertainment,
slug: "entertainment
},
{
name: Kids,
slug: kids
},
]
}
In the Docs I states that I need to use columnHelper.accessor to extract primitive values for each item in your data array.
My question is : how can I configure my accessor on "categories" to display both of them in my cell (using map I guess) ?

First, you don't need to use columnHelper; you can, for convenience (well, type safety, mostly).
You can do something like this:
cell: info => {
const value = info.getValue() as YourCategoryType[]; //casting may not be required here
return <>{value.map(v => <p key={v.slug}>{v.name}</p>)}</>
},

Related

Objects are not valid as a React child (found: object with keys {name}). If you meant to render a collection of children, use an array instead

I am having trouble on posting the lists of data in my table, I hope you guys can help me with this issue, this is the error i am getting Error: Objects are not valid as a React child (found: object with keys {name}). If you meant to render a collection of children, use an array instead.
const columns = [
{
name: "name",
label: "Name",
options: {
filter: true,
sort: true,
}
},
{
name: "dateregistered",
label: "Date Registered",
options: {
filter: true,
sort: false,
}
},
{
name: "department",
label: "Department",
options: {
filter: true,
sort: false,
}
},
];
const data = [
posts.map(post => [{name: 'post.firstname', dateregistered: 'post.date', department: 'post.department'}])
];
return (
<>
<MUIDataTable
title={"Deactivated Users"}
data={data}
columns={columns}
options={options}
/>
</>
)
I see two problems in your data constant. The first one, the .map method returns an array, so there is no need to wrap that value inside an array keys []. The other problem is that you are wrapping the .map return state object in array keys too, that is why the error of creating an object is displayed, because you are returning arrays inside the main mapped array [[{ name: ... }], [{ name: ... }]].
So basically the solution for your issue would be:
const data = posts.map(post => ({name: 'post.firstname', dateregistered: 'post.date', department: 'post.department'}))
Where the parenthesis allows the map method to directly return the object.
The issue is that data is being created as an array in an array in an array. This is why when you try const data = posts.map(post => ...) the error persists, as there is still an array in an array. After you try the above, also re-write
post => [{name: 'post.firstname', dateregistered: 'post.date', department: 'post.department'}]
to
post => ({name: 'post.firstname', dateregistered: 'post.date', department: 'post.department'})
(Changing the square brackets to round)
P.S When you map over posts to build the data, you build a object with the the key "name" and the value "'post.firstname'". That value is a string literal and not accessing some other JS object (It will make "post.firstname" the value for all the post's. Same goes for the other keys.

Formatting data from a database in TypeScript

I am having trouble with writing the following method on an Angular class. I don't know how to add values from arrayId to the data array in the series object.
getChartOptions() {
const arrayId=[];
const arrayTimestamp=[];
const arrayData=[];
const arrayData2=[];
var i=0;
this.httpClient.get<any>('http://prod.kaisens.fr:811/api/sleep/?deviceid=93debd97-6564-454b-be33-35bd377a2563&startdate=1612310400000&enddate=1614729600000').subscribe(
reponse => {
this.sleeps = reponse;
this.sleeps.forEach(element => { arrayId.push(this.sleeps[i]._id),arrayTimestamp.push(this.sleeps[i].timestamp),arrayData.push(this.sleeps[i].data[18]),arrayData2.push(this.sleeps[i].data[39])
i++;
});
console.log(arrayId);
console.log(arrayTimestamp);
console.log(arrayData);
console.log(arrayData2);
}
)
return {
series: [{
name: 'Id',
data: [35, 65, 75, 55, 45, 60, 55]
}]
}
}
I have two main pieces of advice for you:
Know the types of that data that you are dealing with.
Get familiar with all of the various array methods.
get<any>() is not a helpful type. If you understand what the response is then Typescript can help ensure that you are handling it correctly.
I checked out the URL and it looks like you get an array of objects like this:
{
"_id": 4,
"device_id": "93debd97-6564-454b-be33-35bd377a2563",
"timestamp": 1612310400000.0,
"data": "{'sleep_quality': 1, 'sleep_duration': 9}"
},
That data property is not properly encoded as an object or as a parseable JSON string. If you control this backend then you will want to fix that.
At first I thought that the data[18] and data[39] in your code were mistakes. Now I see that it as attempt to extract values from this malformed data. Accessing by index won't work if these numbers can be 10 or more.
The type that you have now is:
interface DataPoint {
_id: number;
device_id: string;
timestamp: number;
data: string;
}
The type that you want is:
interface DataPoint {
_id: number;
device_id: string;
timestamp: number;
data: {
sleep_quality: number;
sleep_duration: number;
}
}
You can type the request as this.httpClient.get<DataPoint[]>( and now you'll get autocomplete on the data.
It looks like what you are trying to do is basically to convert this from one array of rows to a separate array for each column.
You do not need the variable i because the .forEach loop handles the iteration. The element variable in the callback is the row that you want.
this.sleeps.forEach(element => {
arrayId.push(element._id);
arrayTimestamp.push(element.timestamp);
arrayData.push(element.data[18]);
arrayData2.push(element.data[39]);
});
The .forEach loop that you have now is efficient because it only loops through the array once. A .map for each column is technically less efficient because we have to loop through separately for each column, but I think it might make the code easier to read and understand. It also allows Typescript to infer the types of the arrays. Whereas with an empty array you would need to annotate it like const arrayId: number[] = [];.
const mapData = (response: DataPoint[]) => {
return [{
name: 'Id',
data: response.map(element => element._id)
}, {
name: 'Timestamp',
data: response.map(element => element.timestamp)
}, {
name: 'Sleep Quality',
data: response.map(element => parseInt(element.data[18])) // fix this
}, {
name: 'Sleep Duration',
data: response.map(element => parseInt(element.data[39])) // fix this
}]
}
The HTTP request is asynchronous. If you access your array outside of the subscribe callback then they are still empty. I'm not an angular person so this part I'm unsure of, but I think that you want to be updating a property on your class instead of returning the value?
Just follow this piece of code:
series: [{
name: 'Id',
data: arrayId
}]

Why Can't I Use Template Literals to Create Strings as Keys In a New Object? 😕

I have some Array of Objects, with a sample Object as such:
{
id: 10,
name: "Clementina XYZ",
username: "Moriah.Stanton",
email: "foo.bar#quux.biz",
address: {
street: "Kattie Turnpike",
suite: "Suite 555",
city: "Lebsackbury",
zipcode: "31428-2261",
geo: {
lat: "-38.2386",
lng: "57.2232"
}
},
phone: "555-648-3804",
website: "example.com",
company: {
name: "Acme LLC",
catchPhrase: "Centralized empowering task-force",
bs: "target end-to-end models"
}
}
I also have a function that is working just fine to find users that work in the same company.name and map over them to create Objects with name, address and phone.
function buildCompanyDirectory(company, directory) {
return directory
.filter(user => user.company.name === company)
.map(({ name, address, phone }) => ({
name,
address,
phone
}));
}
Now, a new task:
// TODO: Build an Array of company directories
So, I expect to have a final Array consisting of 🔑s that are derived from user.company.name with values that are Arrays of Objects with 🔑s of name, address and phone. So, something like:
[{
"Hoeger LLC": [{ }]
}]
As part of solving this, I feel (evidently JS doesn't agree!) that I should be able to use template literals to create new objects using 🔑s that are derived from the values from another object.
That nonworking code looks like this:
const directories = users.map((user) => {
return {
`${user.company.name}` : buildCompanyDirectory(company.name, users) // ERROR: Property assignment expected
};
});
It doesn't seem that I should need to resort to Object.keys() or anything like that. This feels more like some type of 'syntax' error or just bad notation.
Naturally, I would like to know the simplest way to get this working out! Thanks.
The problem is that key construction in object literals is special-cased in JS. You can use either a literal string or a bare identifier.
To allow an expression as a key in an object literal, JS uses the following hack:
const a = 'aaaa';
const data = {[`this_is_${a}`]: 10}
data.this_is_aaaa === 10; // true.
The expression within [] will be evaluated and used as a key.

Mongoose - Remove several objects from an array (not exact match)

I have a collection Playlist that contains an array of items
{
userId: {
type : String,
required : true,
index : true,
unique : true
},
items: [
{
id: { // do not mix up with _id, which is the autogenerated id of the pair {id,type}. ID is itemId
type : Schema.Types.ObjectId
},
type: {
type : String
}
}
]
}
Mongo automatically adds the _id field to the items when I push a pair {id,type} to items (but I don't care about it).
Now I would like to remove several "pairs" at once from the items array.
I have tried using $pullAll but it requires an exact match, and I do not know the _id, so it does not remove anything from items
playlistModel.update({userId:userId},{$pullAll:{items:[{id:"123",type:"video"},{id:"456",type:"video"}]}},null,function(err){
I have tried using $pull with different variants, but it removed ALL objects from items
playlistModel.update({userId:userId},{$pull:{items:{"items.id":{$in:["123","456"]}}}},null,function(err){
playlistModel.update({userId:userId},{$pull:{items:{$in:[{id:"123",type:"video"},{id:"456",type:"video"}]}}},null,function(err){
Am I missing something or am I asking something that isn't implemented?
If the latter, is there a way I can go around that _id issue?
OK I found a way that works using $pull:
playlistModel.update({userId:userId},{$pull:{items:{id:{$in:["123","456"]}}}},null,function(err){
It doesn't take the type into account but I can't see any issue with that since the id is unique across all types anyway.
Although I will wait a bit to see if someone has a better solution to offer
EDIT
With Veeram's help I got to this other solution, which IMO is more elegant because I don't have _ids that I don't need in the database, and the $pullAll option seems more correct here
var playlistItemSchema = mongoose.Schema({
id: { // do not mix up with autogenerated _id. id is itemId
type : Schema.Types.ObjectId
},
type: {
type : String
}
},{ _id : false });
var schema = new Schema({
userId: {
type : String,
required : true,
index : true,
unique : true
},
items: [playlistItemSchema]
});
playlistModel.update({userId:userId},{$pullAll:{items:[{id:"123",type:"video"},{id:"456",type:"video"}]}},null,function(err){
tips:
you can use _id field to handle your playlistModel data.
mongoose api : new mongoose.Types.ObjectId to generate an Object_id
let _id=new mongoose.Types.ObjectId;
playlistModel.updateMany({_id:_id},{ $set: { name: 'bob' }}).exec(data=>{console.log('exec OK')});

How can I get an item in the redux store by a key?

Suppose I have a reducer defined which returns an array of objects which contain keys like an id or something. What is the a redux way of getting /finding a certain object with a certain id in the array. The array itself can contain several arrays:
{ items:[id:1,...],cases:{...}}
What is the redux way to go to find a record/ node by id?
The perfect redux way to store such a data would be to store them byId and allIds in an object in reducer.
In your case it would be:
{
items: {
byId : {
item1: {
id : 'item1',
details: {}
},
item2: {
id : 'item2',
details: {}
}
},
allIds: [ 'item1', 'item2' ],
},
cases: {
byId : {
case1: {
id : 'case1',
details: {}
},
case2: {
id : 'case2',
details: {}
}
},
allIds: [ 'case1', 'case2' ],
},
}
Ref: http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
This helps in keeping state normalized for both maintaining as well as using data.
This way makes it easier for iterating through all the array and render it or if we need to get any object just by it's id, then it'll be an O(1) operation, instead of iterating every time in complete array.
I'd use a library like lodash:
var fred = _.find(users, function(user) { return user.id === 1001; });
fiddle
It might be worth noting that it is seen as good practice to 'prefer objects over arrays' in the store (especially for large state trees); in this case you'd store your items in an object with (say) id as the key:
{
'1000': { name: 'apple', price: 10 },
'1001': { name: 'banana', price: 40 },
'1002': { name: 'pear', price: 50 },
}
This makes selection easier, however you have to arrange the shape of the state when loading.
there is no special way of doing this with redux. This is a plain JS task. I suppose you use react as well:
function mapStoreToProps(store) {
function findMyInterestingThingy(result, key) {
// assign anything you want to result
return result;
}
return {
myInterestingThingy: Object.keys(store).reduce(findMyInterestingThingy, {})
// you dont really need to use reduce. you can have any logic you want
};
}
export default connect(mapStoreToProps)(MyComponent)
regards

Resources