1

I am trying to convert the keys iterable from a map but when i do so I get the error:

statistics.produceTypeData.keys is not a function

I am following the answer of this question: How to convert Map keys to array? to make it work.

When i try the alternative approach (using Array.from(statistics.produceTypeData.keys())) i get a different error, namely:

Type 'IterableIterator' is not an array type.

In the comments i've read that in this case one should enclose the Array.from() statement in spread syntax but when i do that i also get the error statistics.produceTypeData.keys is not a function.

Here are the code snippets:

  produceTypeLabels: ProduceType[];
      this.produceTypeLabels = [...Array.from(statistics.produceTypeData.keys())]; 

The ProduceType enum:

export enum ProduceType {
    FRUIT = "FRUIT",
    VEGETABLE = "VEGETABLE",
    HERB = "HERB",
    NUT = "NUT",
    SEED = "SEED",
    MUSHROOM = "MUSHROOM",
    CACTUS = "CACTUS"
}

the statistics object comes from a get request and looks like this:

export class DatabaseStatisticsDTO {
    produceTypeData: Map<ProduceType,number>;
    plantTypeData:  Map<PlantType,number>;
    lifeCycleData:  Map<LifeCycleType,number>;
    sunExposureData:  Map<SunExposure,number>;
    ripeColorData:   Map<Color,number>;
    frostResistanceData: Map<boolean,number>;
    teaPlantData: Map<boolean,number>;
    climbingPlantData: Map<boolean,number>; 
    pepperData: Map<boolean,number>; 
}

Does anyone know why i am getting these errors? I know i could simply iterate over the keys iterable and fill an array that way but i would really like to do it with either the spread syntax or the Array.from() function.

Thank you

EDIT:

This is the stacktrace from the debugger when i try to use the spread syntax with statistics.produceTypeData.keys()

RROR TypeError: statistics.produceTypeData.keys is not a function
    at SafeSubscriber._next (database-statistics.component.ts:65)
    at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.__tryOrUnsub (Subscriber.js:195)
    at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.next (Subscriber.js:133)
    at Subscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._next (Subscriber.js:77)
    at Subscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:54)
    at MapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/map.js.MapSubscriber._next (map.js:41)
    at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:54)
    at FilterSubscriber.push../node_modules/rxjs/_esm5/internal/operators/filter.js.FilterSubscriber._next (filter.js:38)
    at FilterSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:54)
    at MergeMapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber.notifyNext (mergeMap.js:79)

EDIT 2: Changed DatabaseStatisticsDTO so that map fields are initialized with empty Map objects, the fields are still undefined after the get request though.

export class DatabaseStatisticsDTO {
    produceTypeData: Map<ProduceType,number> = new Map<ProduceType,number>();
    plantTypeData:  Map<PlantType,number> = new Map<PlantType,number>();
    lifeCycleData:  Map<LifeCycleType,number> = new Map<LifeCycleType,number>();
    sunExposureData:  Map<SunExposure,number> = new Map<SunExposure,number>();
    ripeColorData:   Map<Color,number> = new Map<Color,number>();
    frostResistanceData: Map<boolean,number> = new Map<boolean,number>();
    teaPlantData: Map<boolean,number> = new Map<boolean,number>();
    climbingPlantData: Map<boolean,number> = new Map<boolean,number>();
    pepperData: Map<boolean,number> = new Map<boolean,number>(); 
}

This is the code that makes the get request:

  getStatistics():Observable<DatabaseStatisticsDTO>{
    return this.http.get<DatabaseStatisticsDTO>(this.apiUrl + "/mod/getstatistics");
  }
Maurice
  • 6,698
  • 9
  • 47
  • 104
  • It might help reproduce the problem if you could show us `statistics.produceTypeData` – Mark Jul 20 '18 at 16:40
  • I don't understand the ".keys is not a function" error, but `[...statistics.produceTypeData.keys()]` should work. – ASDFGerte Jul 20 '18 at 16:41
  • statistics is of type DatabaseStatisticsDTO as stated, produceTypeData is a field that holds a Map of – Maurice Jul 20 '18 at 16:41
  • 1
    If it really comes "from a get request" it doesnt have Maps. – Jonas Wilms Jul 20 '18 at 16:41
  • you can't get an object from a getrequest that holds maps? – Maurice Jul 20 '18 at 16:43
  • @ASDFGerte doing that i get the error `error TS2461: Type 'IterableIterator' is not an array type.` – Maurice Jul 20 '18 at 16:44
  • @Jonas W. please look at my DatabaseStatisticsDTO object, this is the object that is being retrieved with the get request. This object holds maps as fields – Maurice Jul 20 '18 at 16:46
  • 1
    Do you perhaps target ES5 or lower? – ASDFGerte Jul 20 '18 at 16:47
  • 2
    in order to create Map you have to create it like via Map constructor. If you haven't done any deserialization on json, all maps in json will be just object literals not Maps – Hikmat G. Jul 20 '18 at 16:47
  • 1
    @Maurice I think you need to log `produceTypeData` to the console (or the debugger) and look at it. It's not what you think it is. – Mark Jul 20 '18 at 16:49
  • ok so i should initialize all the fields in the DatabaseStatistics with an empty map object? @HikmatGurbanli – Maurice Jul 20 '18 at 16:50
  • The error is a compile time TSC error, if he set the variables to hold that type (possibly forcefully from a request), it doesn't matter what they actually are at runtime for this error. EDIT: his current edit contradicts what his reply comment said earlier, and makes what people suggest here plausible again. – ASDFGerte Jul 20 '18 at 16:52
  • and fill them with data manully – Hikmat G. Jul 20 '18 at 16:52
  • @ASDFGerte no as far as i know i'm working with ES6. – Maurice Jul 20 '18 at 16:53
  • @Hikmat Gurbanli how do i fill in the data manually? Can you give me an example? I just found out that all map fields are undefined but the objects are being sent correctly by the backend. – Maurice Jul 20 '18 at 17:06
  • `new Map(Object.entries(objectFromBackend))` converts object literal to Map – Hikmat G. Jul 20 '18 at 17:11
  • @HikmatGurbanli but the object from the backend is a wrapper object that contains all the seperate map objects. The map objects are all made and then stored in the wrapper object and then send to the frontend. So how do i use `Object.entries` in such a case? Thank you – Maurice Jul 20 '18 at 17:17
  • or should i define the fields as object in the wrapper object and then use Object.entries on each field seperately? – Maurice Jul 20 '18 at 17:19
  • iterate over wrapper object, convert 'maps' to Maps – Hikmat G. Jul 20 '18 at 17:24
  • its not an array that can be iterated over. Its a wrapper object named DatabaseStatisticsDTO and each field represents a Map object. and i'd rather not use an array to send all the maps to the frontend. – Maurice Jul 20 '18 at 17:26
  • you can iterate over object using https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys – Hikmat G. Jul 20 '18 at 17:28
  • Object.keys() iterates over field names, shouldn't i use object.values or entries instead? – Maurice Jul 20 '18 at 17:41
  • you can it doesn't really matter, just gave an example, there are lots of methods to do that – Hikmat G. Jul 20 '18 at 17:50

1 Answers1

1

ok i solved the issue. The problem was that i was expecting an object with map objects in it, however what they really contained were object literals that needed to be converted to Map objects first. The code looks like this now:

  this.produceTypeLabels = Object.keys(statistics.produceTypeData); 
  this.produceTypeData  = (<any>Object).values(statistics.produceTypeData).map(a => Number(a));
  this.produceTypeBackgroundColors = ["#9400D3","#0000FF","#00FF00"];

I am using Object.keys to get the key part from the object literal and (<any>Object).values for the values. The reason why i'm using (<any>Object) instead of just Object is because i don't have the ES2017 library installed. I tried doing it but still got a warning that values() wasn't a function of object. As an alternative one can use (<any>Object)

Maurice
  • 6,698
  • 9
  • 47
  • 104