kentcdodds / match-sorter

Simple, expected, and deterministic best-match sorting of an array in JavaScript

Home Page:https://npm.im/match-sorter

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Can't figure out how to generate keys for use with recursive data

dgattey opened this issue · comments

  • match-sorter version: 6.3.1
  • node version: 16.11.1

Relevant code or config

What you did:

I'm trying to write a fuzzy text filter for use with some rows from a react-table instance. Working great! I'm able to pass rows with a generic type of data for each row (TableDataType), and create keys for each of the columns I'm searching on. My ids array are the column names. Some of them have nested keys like name.first, so I'm using a function to generate keys, looking inside the row's values array, then at the column inside it named whatever is in id. So far so good, I'm searching across all columns, even if they have dot notation names, via the functions I return here in topLevelKeys:

export const fuzzyTextFilter = <TableDataType extends PanamaTable.CellDataCollection>(
  rows: Array<Row<TableDataType>>,
  ids: Array<IdType<TableDataType>>,
  filterValue: string,
): Row<TableDataType>[] => {
  if (!ids || !filterValue) {
    return rows;
  }

  /**
 * Map the array of properties to search into an array of functions that take a row and return 
 * the value at that id (inside the `values` object on a row).
 */
  const topLevelKeys: Array<ValueGetterKey<Row<TableDataType>>> = ids.map(
    (id) => (item: Row<TableDataType>) => item.values[id] as string,
  );

  // We may have sub rows, so generate nested keys for those too
  const subRowKeys = generateKeysForSubRows(rows, ids)

  return matchSorter(rows, filterValue, { keys: [...topLevelKeys, ...subRowKeys] });
};

You may notice a subRowKeys and that's where I'm running into problems. Essentially, each of the row's data objects (of type TableDataType here) may have subRows, of type Array<Row<TableDataType>> inside it. I'd ALSO like to generate functions to access the columns in each of those subRows.

Problem description:

Unfortunately, the nesting is theoretically infinite, and because of the possibility of having dots in the column names, I can't just use simple strings recursively till I run into the bottom row. I need to use functions. I've been struggling to figure out a way I can recursively pass a partially curried object that I want to keep pulling objects out of down the recursive stack. I can generate keys at any level (change the item.values[id] to item.subRows.map(subRow => subRow.values[id]), but I can't make that generalized.

So, my long-winded question is, is there a simpler way to do this? All I'm trying to do is search for values within an array, but be able to do so recursively.

Solution:

If it were possible to use the string notation syntax, key: `item.values.${id}` would be possible, and then I could just use a string recursively. However, that breaks because one of the values for id is name.first, which creates item.values.name.first instead of the valid accessor item.values[name.first]. If the array syntax was supported, my problem is solved.

Thanks so much for reading!!

@kentcdodds I took a stab at fixing this within the library, let me know if there's anything I completely missed! Added a new test for the array syntax. Things like key: `item.values[name.first]` works perfectly, as does item.values[0]/etc. Basically anything except the wildcard character can be put into array notation.

const results = matchSorter(searchObjects, filterValue, {
    keys: [(item) => ids.map((id) => item.values[id])],
  });

This ended up solving it - I mapped my rows to searchObjects by attaching some metadata to them, then was able to use this with react-table's filter to get the relevant search objects out, then used the metadata to return the correct data. More my code, less matchSorter code, but the callback version of the keys was key. Thanks @kentcdodds for the help!

I should have suggested that from the start. Thanks!