我有一些对象集合,它们之间有基本的一对多的关系。我的目标是编写一个函数(或必要时可以组合的函数),以便将外部ID字段解析/注入到外部对象。
例如,我有以下对象:
const store = {
users: [
{
teamId: 'team-1',
name: 'user 1',
},
{
teamId: 'team-2',
name: 'user 2',
},
],
teams: [
{
id: 'team-1',
regionId: 'region-1',
name: 'Team 1',
},
{
id: 'team-2',
regionId: 'region-2',
name: 'Team 2',
}
],
regions: [
{
id: 'region-1',
name: 'Region 1',
},
{
id: 'region-2',
name: 'Region 2',
},
],
}我的目标是解决以下问题:
const users = [
{
teamId: 'team-1',
name: 'user 1',
team: {
id: 'team-1',
regionId: 'region-1',
region: {
id: 'region-1',
name: 'Region 1',
},
name: 'Team 1',
}
},
// ...and so on
]我离解决第一个问题不远了:
const findObject = (collection, idField = 'id') => id => R.find(R.propEq(idField, id), R.prop(collection, store))
const findTeam = findObject('teams')
const findRegion = findObject('regions')
const inject = field => R.useWith(
R.merge,
[R.objOf(field), R.identity]
)
const injectTeam = R.useWith(
inject('team'),
[findTeam]
)
const injectRegion = R.useWith(
inject('region'),
[findRegion]
)
R.map(injectTeam('team-1'))(store.users)但这对我来说实在是太过分了,到目前为止,我只对兰达做了更简单的事情。理想情况下,该解决方案将允许我以某种方式组合注入器函数,因此解决更深层次的问题将是可选的。
发布于 2019-04-25 14:28:50
Ramda是在ES5时代编写的,需要支持ES3。自从引入ES6以来,Ramda确实对旧JS进行了许多改进,现在在普通JS中更容易了。请注意,我是Ramda的创始人和狂热粉丝,但是破坏、箭头函数、默认参数、模板字符串和许多其他东西使得跳过Ramda变得更干净。
下面是一个简单的JS解决方案:
const denormalize = (steps) => (store) =>
steps.reduce(
(s, [key, foreignKey, target, targetId, newKey]) => ({
...s,
[key]: s[key].map(({[foreignKey]: fk, ...rest}) => ({
...rest,
[newKey]: s[target].find(x => x[targetId] == fk)
}))
})
, store
)
const updateStore = denormalize([
['teams', 'regionId', 'regions', 'id', 'region'],
['users', 'teamId', 'teams', 'id', 'team'],
])
const store = {users: [{teamId: "team-1", name: "user 1"}, {teamId: "team-2", name: "user 2"}], teams: [{id: "team-1", regionId: "region-1", name: "Team 1"}, {id: "team-2", regionId: "region-2", name: "Team 2"}], regions: [{id: "region-1", name: "Region 1"}, {id: "region-2", name: "Region 2"}]}
console.log(updateStore(store).users)
请注意,这会执行所有的去正化操作,返回一个包含所有非规范化数据的对象。我们只是从它中提取users。显然,我们可以再添加一个包装器,只返回我们想要的部分,但这似乎仍然是有用的。(因此,如果您愿意的话,可以得到非规范化的teams属性。)
这比您的请求更进一步,省略外键并将其替换为外来对象。它只是基于对你想要的东西的误解,或者认为这是我想要的( ;-)。结果如下:
[
{
name: "user 1",
team: {
id: "team-1",
name: "Team 1",
region: {
id: "region-1",
name: "Region 1"
}
}
}, /*... */
]如果您想保留这些外键,代码会更简单一些:
const denormalize = (steps) => (store) =>
steps.reduce(
(s, [key, foreignKey, target, targetId, newKey]) => ({
...s,
[key]: s[key].map(t => ({
...t,
[newKey]: s[target].find(x => x[targetId] == t[foreignKey])
}))
})
, store
)在steps参数中,所有这些字符串的含义可能是模糊的。如果是这样的话,您可以用以下内容替换它:
const config = [
{key: 'teams', foreignKey: 'regionId', target: 'regions', targetId: 'id', newKey: 'region'},
{key: 'users', foreignKey: 'teamId', target: 'teams', targetId: 'id', newKey: 'team'},
]只需将reduce的第一行更改为
(s, {key, foreignKey, target, targetId, newKey}) => ({(这只是从[ ... ]到{ ... }的一个变化。)
发布于 2019-04-24 22:57:52
我使用R.converge提取users并创建teams和regions的查找,然后通过从查找中替换teamId来映射users,并在内部对区域进行同样的操作。
const { pipe, pick, map, indexBy, prop, converge, assoc, identity, flip, evolve } = R
// create a lookup of id -> object from teams and regions
const createLookup = pipe(
pick(['teams', 'regions']),
map(indexBy(prop('id')))
)
// add the value from the idPath in the lookup to the resultPath of the current object
const injectFromLookup = (idKey, lookup, resultKey) =>
converge(assoc(resultKey), [
pipe(
prop(idKey),
flip(prop)(lookup),
),
identity,
])
// extract users, create lookup, and map users to the required form
const inject = converge(
(users, lookup) => map(
pipe(
injectFromLookup('teamId', prop('teams', lookup), 'team'),
evolve({
team: injectFromLookup('regionId', prop('regions', lookup), 'region')
})
)
, users),
[prop('users'), createLookup],
)
const store = {"users":[{"teamId":"team-1","name":"user 1"},{"teamId":"team-2","name":"user 2"}],"teams":[{"id":"team-1","regionId":"region-1","name":"Team 1"},{"id":"team-2","regionId":"region-2","name":"Team 2"}],"regions":[{"id":"region-1","name":"Region 1"},{"id":"region-2","name":"Region 2"}]}
console.log(inject(store))<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
发布于 2019-04-24 22:59:36
下面是使用民间传说/也许的一种可能的方法。我要从高级函数makeUser开始,然后从这里开始-
const makeUser = (user = {}) =>
injectTeam (user)
.chain
( u =>
injectRegion (u.team)
.map
( t =>
({ ...u, team: t })
)
)
.getOrElse (user)每个injector都接受一个get函数、一个set函数和store -
const injectTeam =
injector
( o => o.teamId
, o => team => ({ ...o, team })
, store.teams
)
const injectRegion =
injector
( o => o.regionId
, o => region => ({ ...o, region })
, store.regions
)泛型injector尝试使用get然后set -
const injector = (get, set, store = []) => (o = {}) =>
find (get (o), store) .map (set (o))现在我们实现find,以便它返回一个可能-
const { Just, Nothing } =
require ("folktale/maybe")
const fromNullable = v =>
v == null
? Nothing ()
: Just (v)
const find = (id = "", vs = []) =>
fromNullable (vs .find (v => v.id === id))把所有的东西都放在一起,现在我们只把makeUser命名为store.users中的每个项目-
store.users .map (makeUser)输出
[ { teamId: "team-1"
, name: "user 1"
, team: { id: "team-1"
, regionId: "region-1"
, name: "Team 1"
, region: { id: "region-1"
, name: "Region 1"
}
}
}
, { teamId: "team-2"
, name: "user 2"
, team: { id: "team-2"
, regionId: "region-2"
, name: "Team 2"
, region: { id: "region-2"
, name: "Region 2"
}
}
}
]https://stackoverflow.com/questions/55835963
复制相似问题