0
\$\begingroup\$

I'm wondering if there is a nicer way to acomplish what I want:

I do have an array with object like that:

let data = [
  {val1: [1,2,3,4], val2:[6,7,8,9] }
]

What I want is an object like:

let limit = {
  minX: 1, maxX: 4, minY: 6, maxY: 9
};

My Code works but once more I'm wondering if there is a nicer and shorter way to achieve it? Here is my solution:

  determineMinxMax(data: any) {
    let limit = {minX: null, maxX: null, minY: null, maxY: null}
    limit.minX = Math.min(...data.map(item => Math.min(...item.val1)));
    limit.maxX = Math.max(...data.map(item => Math.max(...item.val1)));
    limit.minY = Math.min(...data.map(item => Math.min(...item.val2)));
    limit.maxY = Math.max(...data.map(item => Math.max(...item.val2)));
    return limit;
  }
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

The original code looks more like traditional JS, it does not benefit from typing features provided by the TypeScript language.

The solution I suggest below is not necessarily shorter, but its main goal is to put the things in order using the TS style.

First of all, interfaces can be created to define the structures of the data and limit objects:

interface MyDataItem {
  val1: Array<number>;
  val2: Array<number>;
};

interface Limit {
  minX: number;
  maxX: number;
  minY: number;
  maxY: number;
};

To access the val1 and val2 properties generically, a type and respective constants can be defined:

type PropertyAccessor = (item: MyDataItem) => Array<number>;

const accessorX: PropertyAccessor = item => item.val1;

const accessorY: PropertyAccessor = item => item.val2;

Similarly, to access the min and max values, we can define dedicated mapping constants:

type ValueExtractor = (values: Array<number>) => number;

const extractorMin: ValueExtractor = values => Math.min(...values);

const extractorMax: ValueExtractor = values => Math.max(...values);

Now, the implementation of the determineMinMax function becomes very concise and does not include repetitive stuff:

const determineMinMax = (data: Array<MyDataItem>): Limit => ({
  minX: extractMinMax(data, accessorX, extractorMin),
  maxX: extractMinMax(data, accessorX, extractorMax),
  minY: extractMinMax(data, accessorY, extractorMin),
  maxY: extractMinMax(data, accessorY, extractorMax)
});

Finally, we need to implement the extractMinMax function, which contains the main calculation logic:

const extractMinMax = (data: Array<MyDataItem>, 
                       propertyAccessor: PropertyAccessor, 
                       minMaxExtractor: ValueExtractor): number =>
  data.map(propertyAccessor) ​// extract the array with the current property from data
     ​.map(minMaxExtractor)   // find the min value of each property
     ​.reduce((prev: number, next: number) => minMaxExtractor([prev, next])); // find the min value among all min values

...and a test case checking the calculated value:

describe('Q268329', () => {

 ​it('should return expected value', () => {
   ​const data = [
     ​{val1: [1,2,3,4], val2:[6,7,8,9] }
   ​];
   ​const limit = determineMinMax(data);
   ​expect(limit).toEqual({
     ​minX: 1, maxX: 4, minY: 6, maxY: 9
   ​});
 ​});

});
\$\endgroup\$
2
  • 1
    \$\begingroup\$ I noticed you are consistently using Array<T>. I only have limited experience with TypeScript, but in my limited experience, it seems that T[] is a more idiomatic way to write that. \$\endgroup\$ Commented Sep 26, 2021 at 12:02
  • \$\begingroup\$ @"Jörg W Mittag" There is no "idiomatic" way between Array and [], both are similar. It's simply a choice of coding style. \$\endgroup\$
    – Antot
    Commented Sep 26, 2021 at 14:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.