Transformation of Observable - javascript

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

Related

property does not exist on type [] typescript

Hey I am trying to loop through an array and find a match to a value however I am having some typescript issues. When using the findIndex function to depict if there is a match in my array I am receiving the following error.
Property 'continents' does not exist on type 'Main[]'
Not exactly sure what's going on here, I am guessing something is wrong with my mapping.
Interfaces:
interface Main {
continents: Array<Continents>;
}
interface Continents {
name: string;
url: string;
countries?: Array<Countries>;
}
interface Countries {
name: string;
url: string;
states: string | null;
featuredCities: Array<Cities>;
}
interface Cities {
name: string;
url: string;
}
State:
const [staticLocationHierarchy, setStaticLocationHierarchy] = useState<Array<Main>>([]);
Business Logic:
const checkOne = staticLocationHierarchy?.continents.findIndex(
continents => continents.url === test
);
I don't know exactly the value you are looking for however, you need to start by cleaning some variables, making your code clean will help typescript know what you want. I just removed and rename some variable I hope it'll help
interface Continent {
name: string;
url: string;
country?: Country[];
}
interface Country {
name: string;
url: string;
states: string | null;
featuredCity: City[];
}
interface City {
name: string;
url: string;
}
const [staticLocationHierarchy, setStaticLocationHierarchy] = useState<Continent[]>([]);
const checkOne = staticLocationHierarchy.filter((continent, index) => {
return continent.url = test
})

How to type function that will infer params with Typescript?

I have a function called invoke which currently is typed like this:
export type InvokeType = <T = InvokeResponseTypes, Params = any>(
methodName: InvokeMethodNameTypes,
args?: Params
) => Promise<T>;
// for context, Invoke is a signalR method to call methods on a SignalR server.
InvokeMethodNameTypes are simply strings that call methods, like "GetUser" or "GetEvent" etc.
Each InvokeMethodNameType has corresponding args (Params) or none at all and I would use it like so:
const data = await invoke<GetEventResponseType, GetEventArgs>("GetEvent", { eventId })
This works but I do not feel like it is the correct approach to what I actually want to achieve.
Is it possible to type the invoke function so when passing a string such as "GetEvent" the arguments are automatically inferred to the correct object type?
Based on the code in the sandbox:
function myFunction(stringArg: 'person'): { name: string, age: number }
function myFunction(stringArg: 'car'): { make: string, model: string }
function myFunction(stringArg: any): any
{
if (stringArg === "person") {
return {
name: "John",
age: 20
};
}
if (stringArg === "car") {
return {
make: "Mazda",
model: "6"
};
}
}
let personResult = myFunction("person");
let personName = personResult.name; // Okay
let personAge = personResult.age; // Okay
let personMake = personResult.make; // Error
let personModel = personResult.model; // Error
let carResult = myFunction("car");
let carName = carResult.name; // Error
let carAge = carResult.age; // Error
let carMake = carResult.make; // Okay
let carModel = carResult.model; // Okay
Playground

How to define an unknown object property key in typescript

With the Vue composition API we created the following composable:
import { computed, reactive, SetupContext, ref } from '#vue/composition-api'
export const useApplications = (root: SetupContext['root']) => {
const applications = reactive({
1: {
name: ref(root.$t('app1.name')),
description: ref(root.$t('app1.description')),
formName: 'app1form',
},
2: {
name: ref(root.$t('app2.name')),
description: ref(root.$t('app2.description')),
formName: 'app2form',
},
})
const getApplication = (id: string) => {
return applications[id]
}
return {
applications: computed(() => applications),
getApplication,
}
}
Although the code works fine it generates the TS error:
#typescript-eslint/no-unsafe-return: Unsafe return of an any typed value
When hovering over the applications section it's clear that typescript recognizes the types except for the property name (1):
Do we need to create an interface to solve this and do we have to redefine each and every property in the interface? I tried something like this but it is incorrect:
interface IApplication {
[key]: string {
name : string
description: string
formName: string
}
}
TypeScript doesn't generally type things so they can be indexed by any string key. As you say, you can define an interface for it:
interface IApplications {
[key: string]: {
name : string;
description: string;
formName: string;
};
}
// ...
const applications: IApplications = ...
Or you might just use a type for the object part of that and use the built-in Record type for applications:
interface IApplication {
name : string;
description: string;
formName: string;
}
// ...
const applications: Record<string, IApplication> = ...
Or combining the two:
interface IApplication {
name : string;
description: string;
formName: string;
}
type IApplications = Record<string, IApplication>;
(Or you can inline the IApplication part. Or... :-) )
According to the structure of the parameters of reactive function, the interface could be defined like :
interface IApplication {
[key:string]:{
name : string
description: string
formName: string
}
}
and
const applications = reactive<IApplication>({ ...
But I want to suggest another approach which define tha applications as reactive parameter which has an array as value:
import { computed, reactive, SetupContext, ref ,toRef} from '#vue/composition-api'
interface IApplication {
name : string
description: string
formName: string
}
export const useApplications = (root: SetupContext['root']) => {
const state= reactive<Array<IApplication>>({ applications :
[{
name: ref(root.$t('app1.name')),
description: ref(root.$t('app1.description')),
formName: 'app1form',
},
{
name: ref(root.$t('app2.name')),
description: ref(root.$t('app2.description')),
formName: 'app2form',
}],
})
const getApplication = (index: number) => {
return state.applications[index]
}
return {
applications: toRef(state,'applications'),
getApplication,
}
}

Correct mapping to interface when getting data from a given URL

I am new to Angular and I am trying to map the returned json to my interface but failed.
Here's the link to the code I have.
Stackblitz
Json URL
You dont need to use map since you response is object and as i see you want to map data property from that.
One way to do it would be
getTestData(): Observable<TestObject>{
const url = 'https://api.myjson.com/bins/11x4bx';
// return this.http.get(url).pipe(
// map(response => ((response || {} as any).data || []).map(testData => this.mapTest(testData))));
return this.http.get(url).pipe(
map((resp: any) => {
return <TestObject> {
id: resp.data.id,
view: resp.data.view,
resourceName: resp.data.resourcename,
loadCapacity: resp.data.loadcapacity
}
})
);
}
Cleaner way
getTestData(): Observable<TestObject>{
const url = 'https://api.myjson.com/bins/11x4bx';
return this.http.get(url).pipe(pluck('data'));
}
When you are not sure about how to build an interface for your response, you can use JSON2TS
declare module namespace {
export interface Meta {
entityName: string;
}
export interface Data {
id: string;
view: string;
startdate: string;
enddate: string;
erp: string;
loadcapacity: string;
userid: string;
resourceid: string;
resourcename: string;
userdatalimitationid: string;
requestleaveforself: boolean;
bookforself: boolean;
lastmodified: string;
}
export interface RootObject {
meta: Meta;
data: Data;
}
}
Here is how you should change your code. You should use map when your data is an array. Your response data is just an object
getTestData(): Observable<TestObject>{
const url = 'https://api.myjson.com/bins/11x4bx';
return this.http.get(url).pipe(
map((res: any) => {
console.log(res)
return {
id: res.data.id,
view: res.data.view,
resourceName: res.data.resourcename,
loadCapacity: res.data.loadcapacity
} as TestObject
})
);
}
You are trying to use map twice.
The first map from RxJS is to do data transformation. In this case you are sending through the pipe an Object, not an array. So you don't need to use the second map since that's a method to iterate through an array.
return this.http.get(url).pipe(
map((resp: any) =>
({
id: resp.data.id,
view: resp.data.view,
resourceName: resp.data.resourcename,
loadCapacity: resp.data.loadcapacity
}) as TestObject)
);
}
Your wiki:
https://stackblitz.com/edit/angular-httpclient-get-1dg2r2
FYI in reactive programming is better to do the Declarative approach
wait, what is that?
testObject$ = this.http.get(url).pipe(
map((resp: any) =>
({
id: resp.data.id,
view: resp.data.view,
resourceName: resp.data.resourcename,
loadCapacity: resp.data.loadcapacity
}) as TestObject)
);
}
then in your component you call it testService.testObject$
You see what I did I got rid of the method and here's why:
-Leverage the power of rxjs observables and opeartors
-effectively compatible streams
-easy share observables
-readily react to user action
#Deborah Kurata mentioned this in his RxJS course (she is a GDE on Angular)

typescript loop through object and create a new object by transforming the values

Given the following Object,
const document = {
id: 'f8bbe6dd-25e3-464a-90e2-c39038d030e5',
fields: {
lastname: 'TestLastName',
firstname: 'TestFirstName'
}
}
how can I transform it to an object of the interface Hit using typescript/javascript?
export interface Hit {
id: string;
fields: { [key: string]: string[] };
}
Expected result is as follows.
document = {
id: 'f8bbe6dd-25e3-464a-90e2-c39038d030e5',
fields: {
lastname: [
'TestLastName'
],
firstname: [
'TestFirstName'
]
}
}
Write a little function which maps object properties, sort of like map, but for objects.
type Hash<T> = {[index: string]: T};
function map<T, U>(
obj: Hash<T>,
fn: (val: T, prop?: string, obj?: any) => U,
thisObj?
): Hash<U> {
const result: Hash<U> = {};
Object.keys(obj).forEach(key => result[key] = fn.call(thisObj, obj[key], key, obj));
return result;
}
Then apply this to your fields property:
function transform(obj): Hit {
const {id, fields} = obj;
return {id, fields: map(obj.fields, x => [x])};
};
This will work if you don't need a more generic solution:
newDocument = {id: document.id, fields: {lastname: [document.fields.lastname], firstname: [document.fields.firstname]} }
You can simply split
export interface Hit {
id: string;
fields: Field;
}
export interface Field {
[index: string]:string[];
}
Inspired from below answer you can see at another stachoverflow answer
export interface IMeta{}
export interface IValue{}
export interface IFunkyResponse {
[index: string]:IValue[];
}
export interface IResponse {
meta: IMeta;
}
export class Response implements IResponse {
meta:IMeta;
values:IValue[];
books:IValue[];
anything:IValue[];
}

Categories