Using angularjs, I want to change what is being used as the label. I have objects which are either companies or people, and use a different value to display as the label. So with the example below, my dropdown options should read: Aida Whitburg, Jones Investments, Edison Yuen, using either the name as the value if it's a person or companyName if the entry is a company.
{ id: 1,
name: "Aida Whitburg",
type: "person"
},
{ id: 2,
companyName: "Jones Investments",
type: "company"},
{ id: 3,
name: "Edison Yuen",
type: "person"
}
<select id="entityDropdown"
ng-options="entity as entity.name for entity in ctrl.entities"
ng-model="ctrl.model.markedEntity" class="form-dropdown"
ng-change="ctrl.onChangeModel()">
</select>
make a new object with merged field and use it, something like that:
let people = [
{
"id": 1,
"name": "Aida Whitburg",
"type": "person"
},
{
"id": 2,
"companyName": "Jones Investments",
"type": "company"
},
{
"id": 3,
"name": "Edison Yuen",
"type": "person"
}
].map(p => {
p.label = p.name || p.companyName
return p;
});
console.log(people)
Related
I am creating an array of objects in TypeScript:
userTestStatus xxxx = {
"0": { "id": 0, "name": "Available" },
"1": { "id": 1, "name": "Ready" },
"2": { "id": 2, "name": "Started" }
};
Can someone tell me how I could declare its type correctly? Is it possible to do inline or would I need two definitions?
I'm looking to replace the xxx with a type declaration, so that later on TypeScript would alert me if I used something like userTestStatus[3].nammme by mistake.
You are better off using a native array instead of an object literal with number-like properties, so that numbering (as well as numerous other array functions) are taken care of off-the-shelf.
What you are looking for here is an inline interface definition for your array that defines every element in that array, whether initially present or introduced later:
let userTestStatus: { id: number, name: string }[] = [
{ "id": 0, "name": "Available" },
{ "id": 1, "name": "Ready" },
{ "id": 2, "name": "Started" }
];
userTestStatus[34978].nammme; // Error: Property 'nammme' does not exist on type [...]
If you are initializing your array with values right away, the explicit type definition is not a necessity; TypeScript can automatically infer most element types from the initial assignment:
let userTestStatus = [
{ "id": 0, "name": "Available" },
...
];
userTestStatus[34978].nammme; // Error: Property 'nammme' does not exist on type [...]
What you have above is an object, not an array.
To make an array use [ & ] to surround your objects.
userTestStatus = [
{ "id": 0, "name": "Available" },
{ "id": 1, "name": "Ready" },
{ "id": 2, "name": "Started" }
];
Aside from that TypeScript is a superset of JavaScript so whatever is valid JavaScript will be valid TypeScript so no other changes are needed.
Feedback clarification from OP... in need of a definition for the model posted
You can use the types defined here to represent your object model:
type MyType = {
id: number;
name: string;
}
type MyGroupType = {
[key:string]: MyType;
}
var obj: MyGroupType = {
"0": { "id": 0, "name": "Available" },
"1": { "id": 1, "name": "Ready" },
"2": { "id": 2, "name": "Started" }
};
// or if you make it an array
var arr: MyType[] = [
{ "id": 0, "name": "Available" },
{ "id": 1, "name": "Ready" },
{ "id": 2, "name": "Started" }
];
Some tslint rules are disabling use of [], example message: Array type using 'T[]' is forbidden for non-simple types. Use 'Array<T>' instead.
Then you would write it like:
var userTestStatus: Array<{ id: number, name: string }> = Array(
{ "id": 0, "name": "Available" },
{ "id": 1, "name": "Ready" },
{ "id": 2, "name": "Started" }
);
Array<T>
person: Array<{
name: string;
age: number;
}>
What you really want may simply be an enumeration
If you're looking for something that behaves like an enumeration (because I see you are defining an object and attaching a sequential ID 0, 1, 2 and contains a name field that you don't want to misspell (e.g. name vs naaame), you're better off defining an enumeration because the sequential ID is taken care of automatically, and provides type verification for you out of the box.
enum TestStatus {
Available, // 0
Ready, // 1
Started, // 2
}
class Test {
status: TestStatus
}
var test = new Test();
test.status = TestStatus.Available; // type and spelling is checked for you,
// and the sequence ID is automatic
The values above will be automatically mapped, e.g. "0" for "Available", and you can access them using TestStatus.Available. And Typescript will enforce the type when you pass those around.
If you insist on defining a new type as an array of your custom type
You wanted an array of objects, (not exactly an object with keys "0", "1" and "2"), so let's define the type of the object, first, then a type of a containing array.
class TestStatus {
id: number
name: string
constructor(id, name){
this.id = id;
this.name = name;
}
}
type Statuses = Array<TestStatus>;
var statuses: Statuses = [
new TestStatus(0, "Available"),
new TestStatus(1, "Ready"),
new TestStatus(2, "Started")
]
A little old but I felt I could add some clarity to this.
Exact Answer
interface MyObject {
id: number;
name: string;
}
interface MyExactData {
[key: string]: MyObject;
}
let userTestStatus: MyExactData = {
"0": { "id": 0, "name": "Available" },
"1": { "id": 1, "name": "Ready" },
"2": { "id": 2, "name": "Started" }
};
But the above is not how we usually do an array of objects, you would use the simple native array in javaScript.
interface MyObject { // define the object (singular)
id: number;
name: string;
}
let userTestStatus_better: MyObject[] = [
{ "id": 0, "name": "Available" },
{ "id": 1, "name": "Ready" },
{ "id": 2, "name": "Started" }
];
Simply adding [] to our interface provides the typing for an array of said objects.
And to do that inline
let userTestStatus_inline: {id:number, name:string}[] = [
{ "id": 0, "name": "Available" },
{ "id": 1, "name": "Ready" },
{ "id": 2, "name": "Started" }
];
I would use the interface as you have something that is definable, understandable, and reusable. If you need to make changes, you can make changes to the one interface and typescript will report your interface to code mismatches.
You can also try
interface IData{
id: number;
name:string;
}
let userTestStatus:Record<string,IData> = {
"0": { "id": 0, "name": "Available" },
"1": { "id": 1, "name": "Ready" },
"2": { "id": 2, "name": "Started" }
};
To check how record works: https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkt
Here in our case Record is used to declare an object whose key will be a string and whose value will be of type IData so now it will provide us intellisense when we will try to access its property and will throw type error in case we will try something like userTestStatus[0].nameee
var xxxx : { [key:number]: MyType };
I have a search bar where you type employee name and it should return the name based on a filter. I have a nested JSON object (as shown below) where I need to drill into the object to access the employee's names in the array.
My problem is the code is not filtering the names and returning all the names not the names searched for. I get this error TypeError: Cannot read property 'filter' of undefined
Question: I need to map the element twice, how would I do this?
UPDATE: No answers helped me. The 2nd answer has to do with querying and is very complicated for the simple answer I am looking for, and the 1st answer has a different data structure than mine.
SECOND UPDATE: Click here for the soultion
I also added this to the thread below
added the below for more clarity
WHAT i have tried:
const results = company.filter((comp) =>
comp.details.employee.toLowerCase().includes(searchField.toLowerCase())
);
const results = company.map((el) => {
return {...el, dets: el.dets.filter((details) =>
details.employee.toLowerCase().includes(searchField.toLowerCase()))}
})
const results = company.filter((comp) => r.details.map((innerArr) => {
return innerArr.employee.toLowerCase().includes(searchField.toLowerCase());
})
);
const results = company.filter((comp) => {
return res.details.map((inner) =>
inner.employee.toLowerCase().includes(searchField.toLowerCase())
);
});
added the below for more clarity
The following code works to access the employee names in another component: how can I implement the above in the below code
{test.map((result) => (result.details.map((innerArr) =>
<h5>{innerArr.employee}</h5>
)))}
const SearchByEmpComp = () => {
const [company, setCompany] = useState([
{
"company": "HIJ",
"_id": "1",
"details":
[
{
"employee": "Lesley Peden",
"notes": "Lesley's note",
"_id": "2"
}
],
},
{
"company": "ABC",
"_id": "3",
"details":
[
{
"employee": "David Barton",
"notes": "some note!!",
"_id": "4"
}
],
}
]);
//below code I need to edit with nested map
const test = company.filter((r) =>
r.details.map((innerArr) => {
return innerArr.employee.toLowerCase().includes
(searchField.toLowerCase());
})
);
const deatils = () => {
if (searchShow)
return <EmpDetailsByName test={test} />
}
};
return (
<>
<FormControl
type="search"
/>
<div>
<Button
onClick={handleClick}
>
Enter
</Button>
<div>{deatils()}</div>
</div
);
};
Something like this should work:
const searchField = 'Les';
const company = [
{
company: 'ABC',
details: [
{
employee: 'Lesley Peden',
_id: 2
}
]
},
{
company: 'EFG',
details: [
{
employee: 'Wayne Smith',
_id: 2
}
]
},
];
const results = company.find(comp =>
comp.details.filter(inner =>
inner.employee.toLowerCase().includes(searchField.toLowerCase())
));
console.log(results);
And print it out in a similar fashion in HTML.
all credit due to Zachary Haber:
This is the CORRECT solution to my question
const test = company.map((element) => {
return {
...element,
details: element.details.filter((details) =>
details.employee.toLowerCase().includes(searchField.toLowerCase())
),
};
});
The OP's company array syntax definitely is broken, especially the details array.
I suppose the latter is going to contain many employee items; anything else does not really make sense.
Also, a Jane Smith or a John Doe have to exist each exactly once per company but of cause can occur each in more than just one company at the same time. This too is a real world use case.
Thus an assumed correct data source format would look more like ...
[{
company: "ABC",
details: [{
employee: "Lesley Peden",
_id: 1,
}, {
employee: "John Doe",
_id: 2,
}, {
employee: "Jane Smith",
_id: 3,
}],
}, {
company: "EFG",
details: [{
employee: "John Doe",
_id: 1,
}, {
employee: "Jane Smith",
_id: 2,
}, {
employee: "Wayne Smith",
_id: 3,
}],
}];
Thus an approach has to collect and normalize any employee item from each company item's details array. In order to not loose the relation to its company item any employee item does get assigned its related company.
This happens during an initial mapping and standardized item creation process. An employee item like { employee: "Lesley Peden", _id: 1 } which belongs to the company item company: "ABC" gets mapped temporarily into a standardized company employee item ... { "companyName": "ABC", "employeeId": 1, "employee": "Lesley Peden" }.
In a second aggregation/reducer step this temporary list of standardized company employee items gets transformed into a list of employee name based items, where each item does feature an additional companies array which reflects the one name to many companies relationship by containing not just one, but maybe more company items which for a name like John Doe will most probably look like this ...
{
name: "John Doe",
companies: [{
companyName: "ABC",
employeeId: 2,
}, {
companyName: "EFG",
employeeId: 1,
}],
}
An array of such items of cause can be easily filtered by each item's name property via (fragmented) name search queries which the beneath approach will demonstrate by some executable example code ...
function createEmployeeWithBoundCompanyData({ _id:employeeId, ...employeeValue }) {
return { ...this, employeeId, ...employeeValue };
}
function aggregateEmployeeItems(collector, companyEmployee) {
const { employee:name, ...employeeValue } = companyEmployee;
const { index, list } = collector;
const employeeItem = index[name] ??= { name, companies: [] };
if (employeeItem.companies.length === 0) {
list.push(employeeItem);
}
employeeItem.companies.push(employeeValue);
return collector;
}
function collectCompanyEmployees(employeeList, companyItem) {
const { company:companyName, _id:companyId, details } = companyItem;
return employeeList.concat(
details.map(
createEmployeeWithBoundCompanyData,
{ companyName, companyId },
)
);
}
function createListOfCompanyEmployees(companyListsOfEployees) {
return companyListsOfEployees
.reduce(collectCompanyEmployees, [])
.reduce(aggregateEmployeeItems, { index: {}, list: [] }).list;
}
function queryCompanyEmployeesByName(companyListsOfEployees, query) {
return createListOfCompanyEmployees(companyListsOfEployees)
.filter(({ name }) =>
name.trim().toLowerCase().includes(
query.trim().toLowerCase()
)
);
}
console.log(
'createListOfCompanyEmployees(companyList) ...',
createListOfCompanyEmployees(companyList)
);
console.log(
"queryCompanyEmployeesByName(companyList, 'Smith') ...",
queryCompanyEmployeesByName(companyList, 'Smith')
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
const companyList = [{
company: "ABC",
_id: "aaaa-bbbb-cccc-0000",
details: [{
employee: "Lesley Peden",
notes: "some notes about Lesley Peden",
_id: "aaaa-bbbb-cccc-0001",
}, {
employee: "John Doe",
notes: "some notes about John Doe",
_id: "aaaa-bbbb-cccc-0002",
}, {
employee: "Jane Smith",
notes: "some notes about Jane Smith",
_id: "aaaa-bbbb-cccc-0003",
}],
}, {
company: "EFG",
_id: "dddd-eeee-ffff-0000",
details: [{
employee: "John Doe",
notes: "some notes about John Doe",
_id: "dddd-eeee-ffff-0001",
}, {
employee: "Jane Smith",
notes: "some notes about Jane Smith",
_id: "dddd-eeee-ffff-0002",
}, {
employee: "Wayne Smith",
notes: "some notes about Wayne Smith",
_id: "dddd-eeee-ffff-0003",
}],
}];
</script>
I'm using Nodejs with Mongoose package.
Given I've something like this:-
let people = [
{
"_id": 1,
"name": "Person 1",
"pets": [
{
"_id": 1,
"name": "Tom",
"category": "cat"
},
{
"_id": 2,
"name": "Jerry",
"category": "mouse"
}
]
}
]
I want to get only the data of Jerry in pets array using it's _id (result shown below)
{
"_id": 2,
"name": "Jerry",
"category": "mouse"
}
Can I get it without needing to specify the _id of person 1 when using $elemMatch? Right now I code like this:-
const pet = People.find(
{ "_id": "1"}, // specifying 'person 1 _id' first
{ pets: { $elemMatch: { _id: 2 } } } // using 'elemMatch' to get 'pet' with '_id' of '2'
)
And it gave me what I want like I've shown you above. But is there any other way I can do this without needing to specify the _id of it's parent first (in this case, the _id of the people array)
Assuming nested array's _id's are unique you can filter by nested array elements directly:
const pet = People.find(
{ "pets._id": 2 },
{ pets: { $elemMatch: { _id: 2 } } }
)
I have the following structure:
{
id: "1",
invoices: [{ id: "1", balance: 1},{ id: "2", balance: 1}]
},
{
id: "2",
invoices: [{ id: "3", balance: 1},{ id: "4", balance: 1}]
}
I'm getting a list of invoices IDs that i shouldn't update, the rest i need to update the balance to 0.
I'm pretty new to MongoDB and managing to find a way to do it.
Let say you want to update all invoices of id 1 except invoice.id 2 try this one:
db.collection.update(
{ id: "1", "invoices.id": {$ne: 2} },
{
$set: {
"invoices.$[]": { balance: 0 }
}
}
)
First of all, you forgot the quotes around the field names. Your documents should be like this:
{
"id": "1",
"invoices": [{
"id": "1",
"balance": 1
}, {
"id": "2",
"balance": 1
}]
}
I have limited experience with MongoDB, as I learnt it this semester at University. However, here is my solution:
db.collection.update(
{ id: "1" },
{
$set: {
"invoices.0": { id: "1", balance: 0 }
}
}
)
What does this solution do?
It takes the document with id 1. That is your first document.
The $set operator replaces the value of a field with the specified value. (straight out from the MongoDB manual - MongoDB Manual - $set operator).
"invoices.0" takes the first invoice from the invoices array and then it updates the balance to 100.
Replace the word collection from db.collection with your collection name.
Try and see if it works. If not, I'd like someone with more experience to correct me.
LE: Now it works, try and see.
The example from docs about many-to-many relationship supposes that companies would be added after the person was already created.
However, what if the person data comes from server with a list of companies (companies' ids) already?
Is it possible to modify the example so that the following code (or smt. similar) would be possible:
// somewhere before we have a collection of companies defined like this:
// [{id: 1, name: 'ibm'}, {id: 2, name: 'apple'}]
// and than we do:
paul = new Person({
name: 'Paul',
jobs: [1, 2]
})
paul.get('jobs').at(0).get('name') // 'ibm'
When trying to achieve this the same way I'd do with one-to-many relations, I fail:
Companies = Backbone.Collection.extend({model: Company})
companies = new Companies([{id: 1, name: 'ibm'}, {id: 2, name: 'apple'}])
john = new Person({
name: 'John',
jobs: [1]
})
john.get('jobs').toJSON() // []
companies.get(1).get('employees').toJSON() // []
Here's the fiddle you can play with: http://jsfiddle.net/ymr5Z/
Your MAIN problem is that you are trying to add Jobs by ID. You never created any job object though let alone set their id! You only created the companies.
A better way to go about adding jobs is to have an addJob() function which you give a company ID (or company) and it creates the Job model for you.
Full Code to fit your example
and specifically:
var Person = Backbone.RelationalModel.extend({
relations: [{
type: 'HasMany',
key: 'jobs',
relatedModel: Job,
reverseRelation: {
key: 'person'
//includeInJSON: false //if you don't want to show person
}
}],
addJob: function (company) {
this.get('jobs').add(new Job({
company: company
}));
}
});
paul.addJob(1);
paul.addJob(2);
Works perfectly. You might also want to set an includeInJSON to false for your reverse relationship on the Job to exclude the person!
[{
"company": {
"id": 1,
"name": "ibm",
"employees": [null]
},
"person": {
"name": "Paul",
"jobs": [null, {
"company": {
"id": 2,
"name": "apple",
"employees": [null]
}
}]
}
}, {
"company": {
"id": 2,
"name": "apple",
"employees": [null]
},
"person": {
"name": "Paul",
"jobs": [{
"company": {
"id": 1,
"name": "ibm",
"employees": [null]
}
},
null]
}
}]