Create map like data structure - javascript

I have this product model:
export class Product {
id: number;
name: string;
price: number;
url: string;
description: string;
constructor() {
this.id = 1;
this.name = '';
this.price = 0.0;
this.url = '';
this.description = '';
}
}
I would like to create a map like a data structure where I can use the Product id as a key and with that, I can receive the structure that has the product itself and the quantity.
In Java that would be:
Map<Integer, Pair<Product, Integer>> = new HashMap<>();
How do I implement it using TypeScript?

You can use the Record:
type ProductMap = Record<number, {product: Product, quantity: number}>;

How about
type ProductMap = Map<number, {product: Product, quantity: number}>
const myProductMap : ProductMap = new Map();
// or use it directly
const myProductMap2 = new Map<number, {product: Product, quantity: number}>();

You can create custom dictionary like this:
interface Map<T> {
[Key: number]: KeyValuePair<T,number>;
}
interface KeyValuePair<T, Y> {
key: T;
value: Y;
}
var somedata: Map<Product> = {...};

Related

Iterate over object in Typescript to find propety and its value

I am trying to iterate over object that looks like this:
interface Book {
title: string;
author_name: string[];
key: string;
first_publish_year: number;
first_sentence?: string;
isbn?: string[];
lcc?: string[];
lccn?: string[];
olid?: string[];
}
The idea is to get any property that matches the name in array and return this key with its value. Here is what I get so far:
const book : Book = {
title: 'Some title',
author_name: 'author',
key: 'stuff';
first_publish_year: 1994;
first_sentence: 'stuff';
isbn: [];
}
let validKey = "";
let validKeyValue = "";
const validKeys = ['lcc', 'lccn', 'isbn', 'olid']
for (let key of Object.keys(book)) {
if(validKeys.includes(key)) {
validKey = key
validKeyValue = book[key]
break;
}
}
This should work in plain old Javascript, but in Typescript I get this error:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Book'.
No index signature with a parameter of type 'string' was found on type 'Book'.
Is there any way that I can bypass this ? How can I iterate over object properties in Typescript ?
const validKeys: Array<Extract<keyof Book, string>> = [
"lcc",
"lccn",
"isbn",
"olid",
];
const validKey = validKeys.find((x) => x in book);
const validKeyValue = validKey && book[validKey];

How to use Typescript Compiler API to resolve type references?

Suppose I have the following interfaces:
interface Person {
name: string;
}
interface Attendee {
person: Person;
id: number;
}
I have already figured out how to use the compiler API to extract string representations of every property's type, e.g.:
{
Attendee: {
person: "Person",
id: "number"
}
}
Here's how I do it: https://github.com/jlkiri/tsx-ray/blob/master/src/index.ts.
It's a combination of typeToString and getTypeOfSymbolAtLocation of the Type Checker.
However I would like to resolve types likes Person to their definition so that I get:
{
Attendee: {
person: {
name: "string";
},
id: "number"
}
}
Is there API I can use to easily do this, or do I have to implement the logic myself?
Check ts-morph. I recently discovered it and it looks promising.
Here is a minimal code that can do what you want:
import {ClassDeclaration, Project} from 'ts-morph';
const project = new Project({);
project.addSourceFilesAtPaths("src/**/*.ts");
const allSourceFiles = project.getSourceFiles();
allSourceFiles.forEach(sourceFile => {
const classes = sourceFile.getClasses();
classes.forEach(cls => {
console.log(`class ${cls.getName()} {`);
const properties = cls.getProperties();
properties.forEach(prop => {
const type = prop.getType();
if(type.isClassOrInterface()) {
const typeSymbol = type.getSymbol();
console.log(` ${prop.getName()} :
${typeSymbol?.getName()} {`);
const clsDeclaration = typeSymbol?.getDeclarations()[0] as ClassDeclaration;
const members = clsDeclaration.getMembers();
members.forEach(m => {
console.log(` ${m.getText()}`);
});
console.log(` }`);
} else {
console.log(` ${prop.getName()} : ${type.getText()}`);
}
});
console.log(`}`);
});
})
For the following two files:
// ./src/property.ts
class Category {
description: string;
id: number;
}
export default Category;
// ./src/product.ts
import Category from './category';
class Product {
name: string;
price: number;
category: Category;
}
export default Product;
you will get the following printout:
class Category {
description : string
id : number
}
class Product {
name : string
price : number
category : Category {
description: string;
id: number;
}
}

Transformation of Observable

I've a data structure which looks like this:
Observable<Array<LineChart>>
whereby an LineChart is defined like
export interface LineChart {
name?: null | string;
series?: null | Array<DateLineChartEntry>;
}
and an DateLineChartEntry is defined like this:
export interface DateLineChartEntry {
name?: string;
value?: number;
}
where name is string, which contains Date.
For my follow-up operation with this DataStructure i need to convert the DateLineChartEntry to sth. like this:
export interface DateLineChartEntryConverted {
name?: Date;
value?: number;
}
which means, i've to map all DateLineChartEntries like this
DateLineChartEntry => {
name: new Date(name),
value: value
}
My current solutions looks like that:
this.data = getObservable({ body: parameters }).pipe(
map(lca => {
var lcaConverted = [];
for (var lc of lca) {
var name = lc.name
var lcN = {
name: name,
series: []
};
for (var e of lc.series) {
var n = new Date(e.name);
lcN.series.push({
name: n,
value: e.value
});
}
lcaConverted.push(lcN);
}
return lcaConverted;
})
);
Which is pretty ugly and I'm looking for a "nicer" solution to this.
Is there an easy way available to do this by using the initial Observable (and receiving an Observable as output)?
Thanks in advance for your help.
Are you just looking for something a little cleaner? If so look into some of the newer array features instead of writing for:
this.data = getObservable({
body: parameters
}).pipe(
map(lca => lca.map(entry => ({
name: entry.name,
series: entry.series.map(x => ({
name: new Date(x.name),
value: x.value
}))
}))
)
);
Specifically .map: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Angular 6 ES6 initiate object arrays

I'm really new to javascript.
I have a nested class structure where I need to initiate using a json object. My question is how can I initiate array of EventDate objects and assign to this.dates in CustomerEvents constructor
export default class CustomerEvent {
constructor(customer_event: any) {
this.event_title = customer_event.event_title;
this.customer = customer_event.customer;
this.total_budget = customer_event.total_budget;
this.no_of_people = customer_event.no_of_people;
this.dates = /**array of new EventDate(customer_event.dates) **/;
}
event_title: string;
customer: Customer;
total_budget: number;
no_of_people: number;
dates: EventDate[];
}
class EventDate {
constructor(date: any) {
this.start_date = date.start_date;
this.end_date = date.end_date;
}
start_date: Date;
end_date: Date;
}
If someone could help me on this, it'll be really helpful. Thanks
Just assign new empty array, like this:
constructor(customer_event: any) {
this.event_title = customer_event.event_title;
this.customer = customer_event.customer;
this.total_budget = customer_event.total_budget;
this.no_of_people = customer_event.no_of_people;
this.dates = [];
}
If you need to cast incoming array, you can do this:
...
this.dates = customer_event.dates.map(date => new EventDate(date));
...
Angular Style Guide recommends using interfaces for data model instead of classes:
Consider using an interface for data models.
That being said, you can refactor your code like this:
export interface EventDate {
start_date: Date;
end_date: Date;
}
export interface CustomerEvent {
event_title: string;
customer: Customer;
total_budget: number;
no_of_people: number;
dates: EventDate[];
}
Now when it comes to initialization, you can do it something like this:
const customerEvent: CustomerEvent = {
event_title: 'Some Title',
customer: { /*An Object representing a Customer Type*/ }
total_budget: 123,
no_of_people: 456,
dates: [{
start_date: new Date(),
end_date: new Date()
}]
};
Create those instances yourself:
constructor(customer_event: any) {
this.event_title = customer_event.event_title;
this.customer = customer_event.customer;
this.total_budget = customer_event.total_budget;
this.no_of_people = customer_event.no_of_people;
this.dates = customer_event.dates.map(date => new EventDate(date));
}

how to get specific data inside class?

Let's say I have a data class like below.
export class TestData extends JsonObject {
id: string;
name: string;
sold: {
number: number;
price: number;
total: string;
}
}
And I can receive from my component like so:
myData:TestData = new TestData();
this.myData.name = "Test"
How can i set a data for price inside sold?
Like this :
This assumes that sold is already defined.
this.myData.sold.price = your_price;
If not then
this.myData.sold = {};
this.myData.sold.price = your_price;
I would declare a setter method to mutate your sold object like this:
export class TestData extends JsonObject {
id: string;
name: string;
sold: {
number: number;
price: number;
total: string;
}
setSold: (number, price, total) => {
object.assign({}, this.sold, {number, price, total})
}
}
You can call the method like this:
TestData.setSold(number, number, "string");
Don't forget to instanciate your class using the new keyword before trying to change it.
const testData = new TestData();
testData.setSold(1, 5, "total");
Further information
Object.assign

Categories