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

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.

Related

TanStack Table 8 : access nested array in data model

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>)}</>
},

How do you write flexible typescript types/interfaces for client-side documents?

Let's say we have the following models:
const catSchema = new Schema({
name: String,
favoriteFood: { type: Schema.Types.ObjectId, ref: 'FoodType' },
});
const foodType = new Schema({
name: String,
});
Here we can see that favoriteFood on the catSchema is a reference to another collection in our database.
Now, let's say that the getAllCats api does not populate the favoriteFood field because it isn't necessary and therefor just returns the reference id for foodType. The response from the api might look like this:
[
{name: 'Fluffy', favoriteFood: '621001113833bd74d6f1fc8c'},
{name: 'Meowzer', favoriteFood: '621001113833bd74d6f1fc4b'}
]
However, the getOneCat api DOES populate the favoriteFood field with the corresponding document. It might look like this:
{
name: 'Fluffy',
favoriteFood: {
name: 'pizza'
}
}
My question, how does one write a client side interface/type for my cat document?
Do we do this?
interface IFavoriteFood {
name: string
}
interface ICat {
name: string,
favoriteFood: IFavoriteFood | string
}
Say we have a React functional component like this:
const Cat = (cat: ICat) => {
return (
<div>
${cat.favoriteFood.name}
</div>
)
}
We will get the following typescript error :
"Property 'name' does not exist on type 'string | IFavoriteFood'.
Property 'name' does not exist on type 'string'."
So, I have to do something like this to make typescript happy:
const Cat = (cat: ICat) => {
return (
<div>
${typeof cat.favoriteFood === 'string' ? 'favoriteFood is not populated': cat.favoriteFood.name}
</div>
)
}
Do I write two separate interfaces? One for the cat object with favoriteFood as a string for the objectId and one for cat with favoriteFood as the populated object?
interface IFavoriteFood {
name: string
}
interface ICat {
name: string,
favoriteFood: string
}
interface ICatWithFavoriteFood {
name: string,
favoriteFood: IFavoriteFood
}
const Cat = (cat: ICatWithFavoriteFood) => {
return (
<div>
${cat.favoriteFood.name}
</div>
)
}
Would love to hear how people approach this in their codebase. Also open to being pointed to any articles/resources that address this issue.
This:
favoriteFood: IFavoriteFood | string
Is a bad idea and will lead to a lot of ugly code trying to sort out when it's one data type versus the other.
I think a better approach (and one I personally use a lot) would be:
favoriteFoodId: string
favoriteFood?: IFavoriteFood
So favoriteFoodId is always there, and is always a string. And a full favoriteFood object is sometimes there.
Now to use that value is a very simple and standard null check.
const foodName = cat.favoriteFood?.name ?? '- ice cream, probably -'
Note, this does mean changing you schema a bit so that foreign keys are suffixed with Id to not clash with the keys that will contain the actual full association data.
You could extend this to the two interface approach as well, if you wanted to lock things down a bit tighter:
interface ICat {
name: string,
favoriteFoodId: string
favoriteFood?: null // must be omitted, undefined, or null
}
interface ICatWithFavoriteFood extends ICat {
favoriteFood: IFavoriteFood // required
}
But that's probably not necessary since handling nulls in your react component is usually cheap and easy.

How to find whether an object is of type formcontroller in angular js

I have a nested form. I want to validate it recursively. While validating, I need to check whether the object is of type Formcontroller or not. When trying instance of or type of, I am getting it as an object, not the form controller. Kindly help to find this.
If you want to know if an object is of a certain type, I believe you could ask something like this:
person = {
id: 1,
name: 'John',
age: 46,
active: true
};
house = {
color: 'blue with white patterns',
years: 20,
isInGoodCondition: true
};
const myArray = [person, house];
myArray.forEach((element) => {
if (element?.name) console.log('It is a person object');
if (element?.isInGoodCondition) console.log('It is a house object');
});
The object?.property asks safely if that property exists inside your object.

mongoose query: find an object by id in an array

How could I find an image by id in this Schema. I have the id of the User and the id of the image I am looking for. What would be the best way to do this and do all images in this case have different ids or could they have the same id because they don't belong to the same User?
My Schema looks like this:
var userSchema = new Schema({
local: {
email: String,
password: String
},
facebook: {
id: String,
token: String,
email: String,
name: String
},
name: String,
about: String,
images: [{
id: Schema.ObjectId,
link: String,
main: Boolean
}]
});
When you are interested in the full object it is a simple find:
.find({"facebook.id":"<id>", "images.id":<image-id>})
I don't think that there is a way to reduce the image array in the result.
To update a single element in the image array you can use this:
.update({"facebook.id":"<id>", "images.id":<image-id>}, {$set : {"images.$.main" :false} } );
userSchema .find({facebook.id: "some ID",{ "images.id": { $in: [ id1, id2, ...idn] }}
since images are inside the document you can have same ID's however every time you query you should keep in mind that you send some other parameters such as facebook.id or facebook.email along with image id's to retrieve them. Otherwise you end up getting all that might be irrelevant only because you decide to keep same ID's for images.
tl;dr
I struggled with this and came up with a solution. Like you, I was trying to query for a deeply nested object by the _id, but I kept coming up empty with the results. It wasn't until I did some type checking that I realized the id value I was getting from my frontend, while directly supplied by mongoose, was in fact a String and not an Object.
I realize this question was already partially answered before, but that person's solution didn't work for me, and the comment on the answer tells me you wanted to update the specific image you queried for, which is exactly what I was trying to do.
The solution
In order to select an object from the nested array by the _id value, first you'll have to install the npm package bson-objectid and use the provided method to convert your string into an objectId in your query.
In your terminal:
npm i bson-objectid
In your code:
const ObjectId = require('bson-objectid')
userSchema.findOneAndUpdate(
{ "facebook.id": <user-id>, "images._id": ObjectId(<image-id>) },
{ "$set": { "images.$.main": false } },
{ new: true }, // an extra options parameter that returns the mutated document
(err, user) => {
if (err) {
handleErr(err)
} else {
console.log(user)
// do something with new user info
}
)

How to fetch particular objects by their attribute from Array?

I need to get objects with special attribute "type" out of Array. These objects I am going to assign to scope. How can I do this?
The following approach didn't work out for me.
$scope.vendors = {}
$scope.clients = {}
$scope.loadCounterparties = function() {
Counterpartie.query(function(response) {
$scope.vendors = response.type.Vendor;
$scope.clients = response.type.Client
});
};
Response objects look like this
Thanks in advance!
Angular doesn't have something dedicated for this. You need to filter the arrays via plain java script. However you can try using a 3rd party library by the name underscore.js.
It adds many usefull functions like "where":
_.where(list, properties)
Looks through each value in the list, returning an array of all the values that contain all of the key-value pairs listed in properties.
_.where(listOfPlays, {author: "Shakespeare", year: 1611});
=> [{title: "Cymbeline", author: "Shakespeare", year: 1611},
{title: "The Tempest", author: "Shakespeare", year: 1611}]
Here is a link to the library's page
http://underscorejs.org/#where
can use angular forEach but I would use lodash
// assuming one array and two search arguments i.e. client and vendor
var data = response;
$scope.loadCounterparties = _.filter(data, {type: 'Vendor', type: 'Client'});

Resources