Skip to content
This repository has been archived by the owner on Dec 30, 2022. It is now read-only.

Commit

Permalink
refactor(lodash): get (#2461)
Browse files Browse the repository at this point in the history
* refactor(lodash): get

IFW-742

BREAKING CHANGE: no longer do we allow paths like `attribute[5].something`, or other indexed forms, only `.` is allowed as special key.

All existing tests still pass, and we never documented you could use `lodash.get` patterns other than `.`.

* feat(get): accept array & bracked-separated string

moved to utils at the same time

* fix typo

* feedback: test for undefined behaviour

* chore(size): update expectation

this will go down afterwards, but for now there's some more duplication
  • Loading branch information
Haroenv committed Jun 27, 2019
1 parent b84a0b5 commit 527b879
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 2 deletions.
105 changes: 105 additions & 0 deletions packages/react-instantsearch-core/src/core/__tests__/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,111 @@ describe('utils', () => {
});
});

describe('getPropertyByPath', () => {
it('returns undefined on non-object root', () => {
expect(utils.getPropertyByPath(false, 'fake')).toBeUndefined();
expect(utils.getPropertyByPath(undefined, 'fake')).toBeUndefined();
expect(utils.getPropertyByPath(null, 'fake.nested')).toBeUndefined();
});

it('returns path if exists', () => {
expect(utils.getPropertyByPath({ dog: true }, 'dog')).toBe(true);
expect(
utils.getPropertyByPath(
{ i: { like: { properties: false } } },
'i.like.properties'
)
).toBe(false);
expect(
utils.getPropertyByPath({ true: { nested: 'ok' } }, 'true.nested')
).toBe('ok');
});

it('accepts a pre-split path as array', () => {
expect(utils.getPropertyByPath({ dog: true }, ['dog'])).toBe(true);
expect(
utils.getPropertyByPath({ i: { like: { properties: false } } }, [
'i',
'like',
'properties',
])
).toBe(false);
expect(
utils.getPropertyByPath({ true: { nested: 'ok' } }, ['true', 'nested'])
).toBe('ok');
});

it('does not split a pre-split path as array', () => {
expect(utils.getPropertyByPath({ dog: true }, ['dog'])).toBe(true);
expect(
utils.getPropertyByPath({ i: { like: { properties: false } } }, [
'i',
'like.properties',
])
).toBeUndefined();
expect(
utils.getPropertyByPath({ true: { nested: 'ok' } }, ['true.nested'])
).toBeUndefined();
});

it('returns undefined if does not exist', () => {
expect(
utils.getPropertyByPath(
{ name: { known: { value: '' } } },
'name.unkown'
)
).toBeUndefined();

expect(utils.getPropertyByPath({ name: false }, 'name.unkown')).toBe(
undefined
);
});

it('returns indexed path if exists', () => {
expect(
utils.getPropertyByPath({ array: ['a', 'b', 'c'] }, 'array.2')
).toBe('c');
expect(
utils.getPropertyByPath(
{ array: [{ letter: 'a' }, { letter: 'b' }, { letter: 'c' }] },
'array.2.letter'
)
).toBe('c');

expect(
utils.getPropertyByPath({ array: ['a', 'b', 'c'] }, 'array[2]')
).toBe('c');
expect(
utils.getPropertyByPath(
{ array: [{ letter: 'a' }, { letter: 'b' }, { letter: 'c' }] },
'array[2].letter'
)
).toBe('c');
});

it('returns undefined if indexed path does not exist', () => {
expect(
utils.getPropertyByPath({ array: ['a', 'b', 'c'] }, 'array.4')
).toBeUndefined();
expect(
utils.getPropertyByPath(
{ array: [{ letter: 'a' }, { letter: 'b' }, { letter: 'c' }] },
'array.5.letter'
)
).toBeUndefined();

expect(
utils.getPropertyByPath({ array: ['a', 'b', 'c'] }, 'array[4]')
).toBeUndefined();
expect(
utils.getPropertyByPath(
{ array: [{ letter: 'a' }, { letter: 'b' }, { letter: 'c' }] },
'array[5].letter'
)
).toBeUndefined();
});
});

describe('find', () => {
test('returns the first match based on the comparator', () => {
expect(
Expand Down
5 changes: 3 additions & 2 deletions packages/react-instantsearch-core/src/core/highlight.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { get } from 'lodash';
import { getPropertyByPath } from './utils';

export const HIGHLIGHT_TAGS = {
highlightPreTag: `<ais-highlight-0000000000>`,
Expand Down Expand Up @@ -72,7 +72,8 @@ export function parseAlgoliaHit({
}) {
if (!hit) throw new Error('`hit`, the matching record, must be provided');

const highlightObject = get(hit[highlightProperty], attribute, {});
const highlightObject =
getPropertyByPath(hit[highlightProperty], attribute) || {};

if (Array.isArray(highlightObject)) {
return highlightObject.map(item =>
Expand Down
23 changes: 23 additions & 0 deletions packages/react-instantsearch-core/src/core/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,26 @@ export function omit(source: { [key: string]: any }, excluded: string[]) {
}
return target;
}

/**
* Retrieve the value at a path of the object:
*
* @example
* getPropertyByPath(
* { test: { this: { function: [{ now: { everyone: true } }] } } },
* 'test.this.function[0].now.everyone'
* ); // true
*
* getPropertyByPath(
* { test: { this: { function: [{ now: { everyone: true } }] } } },
* ['test', 'this', 'function', 0, 'now', 'everyone']
* ); // true
*
* @param object Source object to query
* @param path either an array of properties, or a string form of the properties, separated by .
*/
export const getPropertyByPath = (object: object, path: string[] | string) =>
(Array.isArray(path)
? path
: path.replace(/\[(\d+)]/g, '.$1').split('.')
).reduce((current, key) => (current ? current[key] : undefined), object);

0 comments on commit 527b879

Please sign in to comment.