extracting the properties of a generic type to retrieve a specific object's attribute

Is it possible to destructure a generic lambda function parameter recursively to check if a property exists within an object? I want to keep the input types of this function as general as possible without extending T to any existing type.

Let's clarify with an example:

const binarySearch = <T, U>(array:T[], value:(T|U), getProperty?:(item:T, index?:number) => U):number => {}

Here, "U" could represent any of "T"s properties.

This function can be called by accessing a property using a lambda if it is an object array:

const index = binarySearch(objectArray, objectProperty, (obj) => obj.property)

Alternatively, it can be called by using the array value if it isn't:

const index = binarySearch(primitiveArray, primitive)

The expectation is for the function to behave like a normal binary search method but with more flexibility in accessing different property types without the need to create multiple similar functions for nested properties.

Answer №1

No special treatment is needed for primitives in this scenario. Simply pass the identity function for getProperty: i => i.

I am sufficiently interested in your goal to devise a universal solution on my own, complete with examples using 1) an array of numbers and 2) an array of objects with a date property:

const binarySearch = <T, U>(
  arr: T[], 
  getProperty:(item:T) => U, 
  searchedValue: U, 
  subArrayHeadIndex = 0, //for internal use w/recursion
): number | undefined => {
  const midPoint = Math.floor(arr.length / 2);
  const item = arr[midPoint];
  const propVal: U = getProperty(item);
  if(propVal === searchedValue){
    return subArrayHeadIndex + arr.indexOf(item);
  }
  const newStart = propVal > searchedValue ? 0 : midPoint + 1;
  const newEnd = propVal > searchedValue ? midPoint : arr.length;
  if (newStart === newEnd){
    console.log("couldn't find it")
    return -1
  }
  const newSubArrayHeadIndex = subArrayHeadIndex + (propVal > searchedValue ?  0: midPoint + 1);
  return binarySearch(arr.slice(newStart, newEnd), getProperty, searchedValue, newSubArrayHeadIndex);
}
  1. Primitives case -- Determine the position of 29 in the following array:
const numbers = [2, 3, 6, 11, 13, 29, 35, 65, 89, 143, 144, 190, 2001];
console.log(binarySearch(numbers, i => i, 29));
// outputs "5"
  1. Object case -- Discover the location of the item dated August 21, 1995 from the array:
const things = [
  { id: 1, date: 'January 17, 1995 03:24:00'},
  { id: 2, date: 'December 25, 1995 11:24:00'},
  { id: 3, date: 'January 27, 1995 05:43:00'},
  { id: 4, date: 'March 1, 1995 09:44:00'},
  { id: 5, date: 'April 10, 1995 13:20:00'},
  { id: 6, date: 'May 2, 1995 07:16:00'},
  { id: 7, date: 'May 24, 1995 15:55:00'},
  { id: 8, date: 'August 17, 1995 21:33:00'},
  { id: 9, date: 'August 21, 1995 21:49:00'},
  { id: 10, date: 'December 17, 1995 19:57:00'},
];

const getDayString = (dateStr: string) => {
  const date = new Date(dateStr)
  //using Korean here for convenient comparison -- the KR format is "yyyy. mm. dd."
  const d= date.toLocaleDateString("ko-KR", {
    year: "numeric",
    month: "2-digit",
    day: "2-digit"
  });
  return d;
}

console.log(binarySearch(things, i => getDayString(i.date), "1995. 08. 21."))
// outputs "8"

Answer №2

While working on other functions, I stumbled upon my own solution.

The getnestedkeys type function does not support nested arrays as I currently have no use for it. However, it could potentially be added as a second ternary after checking if the property type is an object.

I still prefer to make the getproperty parameter conditional to avoid having to always write `i => i` for any calls involving primitive arrays. This can easily be modified though.

Below is the code snippet:

export type getNestedKeyTypes<T extends object> = {
    [K in keyof T]:T[K] extends object ? getNestedKeyTypes<T[K]> : K[keyof K]
}[keyof T]

export const binarySearch = <T, U, V = T extends object ? getNestedKeyTypes<T> : T>
(arr:T[], value:V, getProperty?:(item:T) => (U&V)):number => {
    if(arr.length === 0) return -1
    const getArrayValue = (i:number) => getProperty ? getProperty(arr[i]) : arr[i]
    const getIndex = (upper:number, lower:number):number => {
        if(lower > upper) return -1
        const m = Math.round((upper+lower)/2)
        const av = getArrayValue(m)
        if(value === av) return m
        if(value > av) return getIndex(m-1, lower)
        return getIndex(upper, m+1)
    }
    return getIndex(0,arr.length-1)
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What should I do when dealing with multiple submit buttons in NextJS?

How can I differentiate between two submit buttons in a form component created in Next.js? I'm struggling to determine which button was pressed and need help resolving this issue. import React from "react"; const LoginPage = () => { as ...

Encountering an issue in the test file when using react-router-dom v6: "The 'history' property is not found on the 'IntrinsicAttributes & RouterProps' type."

Main script: import { useContext, useEffect } from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; import AuthenticationContext from './AuthenticationContext'; function HandleOAuthCallbackRoute() { co ...

How can we prevent transform Omit into Pick in Typescript when utilizing generics?

Is there a way to prevent this behavior? It's crucial when compiling type declarations solely for libraries. I specifically require it for compiling declarations only: tsc --declaration true --emitDeclarationOnly true Here is a minimal example of t ...

Typescript throwing error TS2307 when attempting to deploy a NodeJS app on Heroku platform

Encountering an error when running the command git push heroku master? The build step flags an error, even though locally, using identical NodeJS and NPM versions, no such issue arises. All automated tests pass successfully without any errors. How can this ...

Steps to converting an enum or literal

Hey there, I'm relatively new to working with TypeScript and I've been experimenting with transforming enums/literals using functions. For instance, creating a capitalize function that capitalizes the first letter of a string. (e.g., mapping typ ...

Error: The variable "dispatcher.useMemo" is not recognized as an object

I encountered an issue while trying to incorporate InversifyJS with MobX in my React Native project. The error message I received was: ERROR TypeError: null is not an object (evaluating 'dispatcher.useMemo') Surprisingly, I have not utilized ...

What is the best way to test chained function calls using sinon?

Here is the code I am currently testing: obj.getTimeSent().getTime(); In this snippet, obj.getTimeSent() returns a Date object, followed by calling the getTime() method on that Date. My attempt to stub this functionality looked like this: const timeStu ...

The type 'Navigator' does not have the property 'userAgentData' in its definition

Since I'm looking to minimize the information provided by navigator.userAgent, I decided to migrate to User-Agent Client Hints. However, I've encountered an error while attempting to do so: https://i.stack.imgur.com/lgIl7.png Could someone plea ...

NativeScript Error Code NG8001: Element 'ActionBar' is unrecognized

In my project, the startupscreen module setup is as follows: import { NativeScriptFormsModule } from "@nativescript/angular"; import { NativeScriptCommonModule } from "@nativescript/angular/common"; import { NgModule, NO_ERRORS_SCHEMA } ...

The test function is not recognized by Cordova when the device is ready

Within my app.component.ts file, I have the following code snippet: export class AppComponent implements OnInit { constructor(private auth: AuthService, private fireBaseService: FirebaseService, ) {} ngOnInit() { this.auth.run(); document.addE ...

Is there a way to prevent the leaderboard from resetting each time I restart my client?

Is it possible to prevent the leaderboard from resetting every time I restart my client? You can see an example here: https://i.stack.imgur.com/2nEPw.png Please disregard the "undefined" error, I will correct it. In the current setup, the leaderboard onl ...

I encountered an issue while generating a crypto address on the Waves blockchain using the @waves/waves-crypto library in TypeScript

Encountering an issue with finding "crypto-js" in "@waves/waves-crypto". Despite attempts to uninstall and reinstall the module via npm and importing it using "*wavesCrypto", the error persists within the module's index.d.ts file. I am attempting to ...

Describe the process of defining an Interface object array and effectively utilizing it

I have a sample object array structured like this: tasks: todo = [ { title: string, status: number, description: string, date: Date, priority: number } ] To handle this, I created an Interface as foll ...

How can I update a dropdown menu depending on the selection made in another dropdown using Angular

I am trying to dynamically change the options in one dropdown based on the selection made in another dropdown. ts.file Countries: Array<any> = [ { name: '1st of the month', states: [ {name: '16th of the month&apos ...

When `Omit` is applied to a type that includes an index signature, it removes all field declarations that are not part of

Currently, I am working on developing a React component that wraps around the Select component from react-select. The issue arises with a specific prop declaration that can be seen here: export type SelectComponentsProps = { [key in string]: any }; expor ...

Do interfaces in Typescript require nested properties to be mandatory?

My interface contains a nested object: export interface Person { PersonWrapper: { name: string; address: string email?: string; } } When attempting to create an object from this interface, it appears that name is not mandat ...

Where does tsc retrieve its definitions from when utilizing npm definitions?

After transitioning from using typings to just relying on npm, I noticed that the @types directory in node_modules is present, but there are no additional files required. Previously with typings, I always had to include the index.d.ts file within the typi ...

Implementing setState callback in TypeScript with React Hooks

Hello everyone! I am currently working on defining my component types using TypeScript, and I am faced with a challenge. I am trying to set it up so that I can either pass an array of objects or a callback function containing the previous state. Below is t ...

Can Tailwind Merge function apply to Tailwind classes with prefixes?

When dealing with conflicts, I am required to use tailwind classes with a prefix. However, the tailwind merge functionality does not seem to work with prefixed classes such as tw-flex-1 instead of flex-1. During debugging, I attempted the following: conso ...

Display sub-objects within Chart.js

I'm currently tackling a project in Ionic 3 where I am utilizing an API that returns a JSON response with nested objects. My goal is to display certain aspects of these objects within a bar graph using chart.js. Unfortunately, I lack experience in ma ...