Вы можете собрать это вместе, используя множество маленьких функций, но я хочу показать вам, как кодировать ваши намерения более простым способом.Эта программа имеет дополнительное преимущество, которое она вернет немедленно.То есть, не продолжит поиск по дополнительным парам ключ / значение после обнаружения совпадения.
Вот способ, которым вы можете сделать это с помощью взаимной рекурсии.Сначала мы пишем findPath
-
const identity = x =>
x
const findPath =
( f = identity
, o = {}
, path = []
) =>
Object (o) === o
? f (o) === true
? path
: findPath1 (f, Object .entries (o), path)
: undefined
Если вход является объектом, мы передаем его в функцию поиска пользователя f
.Если функция поиска пользователя возвращает true
, совпадение найдено, и мы возвращаем path
.Если совпадений нет, мы ищем каждую пару ключ / значение объекта, используя вспомогательную функцию.В противном случае, если входные данные не объект, совпадения нет и ничего не осталось искать, поэтому верните undefined
.Мы пишем помощник, findPath1
-
const None =
Symbol ()
const findPath1 =
( f = identity
, [ [ k, v ] = [ None, None ], ...more ]
, path = []
) =>
k === None
? undefined
: findPath (f, v, [ ...path, k ])
|| findPath1 (f, more, path)
Если пары ключ / значение были исчерпаны, искать нечего, поэтому верните undefined
.В противном случае у нас есть ключ k
и значение v
;добавьте k
к пути и рекурсивно найдите v
для соответствия.Если совпадения нет, рекурсивно ищите оставшиеся ключи / значения, more
, используя тот же path
.
Обратите внимание на простоту каждой функции.Ничего не происходит, за исключением абсолютного минимального количества шагов для сборки path
для соответствующего объекта.Вы можете использовать его следующим образом -
const opportunitiesById =
{ 1:
[ { id: 1, name: 'offer 1' }
, { id: 2, name: 'offer 1' }
]
, 2:
[ { id: 3, name: 'offer 1' }
, { id: 4, name: 'offer 1' }
]
, 3:
[ { id: 5, name: 'offer 1' }
, { id: 6, name: 'offer 1' }
]
}
findPath (offer => offer.id === 6, opportunitiesById)
// [ '3', '1' ]
Возвращенный путь ведет нас к объекту, который мы хотели найти -
opportunitiesById['3']['1']
// { id: 6, name: 'offer 1' }
Мы можем специализировать findPath
, чтобы сделать интуитивно понятным findByOfferId
function -
const findByOfferId = (q = 0, data = {}) =>
findPath (o => o.id === q, data)
findByOfferId (3, opportunitiesById)
// [ '2', '0' ]
opportunitiesById['2']['0']
// { id: 3, name: 'offer 1' }
Как и Array.prototype.find
, возвращается undefined
, если совпадение не найдено -
findByOfferId (99, opportunitiesById)
// undefined
Разверните фрагмент ниже, чтобы проверить результаты в своем собственном браузере-
const identity = x =>
x
const None =
Symbol ()
const findPath1 =
( f = identity
, [ [ k, v ] = [ None, None ], ...more ]
, path = []
) =>
k === None
? undefined
: findPath (f, v, [ ...path, k ])
|| findPath1 (f, more, path)
const findPath =
( f = identity
, o = {}
, path = []
) =>
Object (o) === o
? f (o) === true
? path
: findPath1 (f, Object .entries (o), path)
: undefined
const findByOfferId = (q = 0, data = {}) =>
findPath (o => o.id === q, data)
const opportunitiesById =
{ 1:
[ { id: 1, name: 'offer 1' }
, { id: 2, name: 'offer 1' }
]
, 2:
[ { id: 3, name: 'offer 1' }
, { id: 4, name: 'offer 1' }
]
, 3:
[ { id: 5, name: 'offer 1' }
, { id: 6, name: 'offer 1' }
]
}
console .log (findByOfferId (3, opportunitiesById))
// [ '2', '0' ]
console .log (opportunitiesById['2']['0'])
// { id: 3, name: 'offer 1' }
console .log (findByOfferId (99, opportunitiesById))
// undefined
В этом , связанном с вопросами и ответами , я демонстрирую рекурсивную функцию поиска, которая возвращает совпавший объект, а не путь к совпадению.Есть и другие полезные трюки, поэтому я рекомендую вам взглянуть на них.
Ответ Скотта вдохновил меня на попытку реализации с использованием генераторов.Мы начинаем с findPathGen
-
const identity = x =>
x
const findPathGen = function*
( f = identity
, o = {}
, path = []
)
{ if (Object (o) === o)
if (f (o) === true)
yield path
else
yield* findPathGen1 (f, Object .entries (o), path)
}
И, используя взаимную рекурсию, как мы делали в прошлый раз, мы вызываем помощник findPathGen1
-
const findPathGen1 = function*
( f = identity
, entries = []
, path = []
)
{ for (const [ k, v ] of entries)
yield* findPathGen (f, v, [ ...path, k ])
}
Наконец, мы можем реализовать findPath
и специализация findByOfferId
-
const first = ([ a ] = []) =>
a
const findPath = (f = identity, o = {}) =>
first (findPathGen (f, o))
const findByOfferId = (q = 0, data = {}) =>
findPath (o => o.id === q, data)
Работает так же -
findPath (offer => offer.id === 3, opportunitiesById)
// [ '2', '0' ]
findPath (offer => offer.id === 99, opportunitiesById)
// undefined
findByOfferId (3, opportunitiesById)
// [ '2', '0' ]
findByOfferId (99, opportunitiesById)
// undefined
И в качестве бонуса, мы можем легко реализовать findAllPaths
, используя Array.from
-
const findAllPaths = (f = identity, o = {}) =>
Array .from (findPathGen (f, o))
findAllPaths (o => o.id === 3 || o.id === 6, opportunitiesById)
// [ [ '2', '0' ], [ '3', '1' ] ]
Проверьте результаты, развернув фрагмент ниже
const identity = x =>
x
const findPathGen = function*
( f = identity
, o = {}
, path = []
)
{ if (Object (o) === o)
if (f (o) === true)
yield path
else
yield* findPathGen1 (f, Object .entries (o), path)
}
const findPathGen1 = function*
( f = identity
, entries = []
, path = []
)
{ for (const [ k, v ] of entries)
yield* findPathGen (f, v, [ ...path, k ])
}
const first = ([ a ] = []) =>
a
const findPath = (f = identity, o = {}) =>
first (findPathGen (f, o))
const findByOfferId = (q = 0, data = {}) =>
findPath (o => o.id === q, data)
const opportunitiesById =
{ 1:
[ { id: 1, name: 'offer 1' }
, { id: 2, name: 'offer 1' }
]
, 2:
[ { id: 3, name: 'offer 1' }
, { id: 4, name: 'offer 1' }
]
, 3:
[ { id: 5, name: 'offer 1' }
, { id: 6, name: 'offer 1' }
]
}
console .log (findByOfferId (3, opportunitiesById))
// [ '2', '0' ]
console .log (findByOfferId (99, opportunitiesById))
// undefined
// --------------------------------------------------
const findAllPaths = (f = identity, o = {}) =>
Array .from (findPathGen (f, o))
console .log (findAllPaths (o => o.id === 3 || o.id === 6, opportunitiesById))
// [ [ '2', '0' ], [ '3', '1' ] ]