首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用Ramda通过id查找和注入外来对象?

如何使用Ramda通过id查找和注入外来对象?
EN

Stack Overflow用户
提问于 2019-04-24 17:57:04
回答 3查看 174关注 0票数 0

我有一些对象集合,它们之间有基本的一对多的关系。我的目标是编写一个函数(或必要时可以组合的函数),以便将外部ID字段解析/注入到外部对象。

例如,我有以下对象:

代码语言:javascript
复制
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',
    },
  ],
}

我的目标是解决以下问题:

代码语言:javascript
复制
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
]

我离解决第一个问题不远了:

代码语言:javascript
复制
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)

但这对我来说实在是太过分了,到目前为止,我只对兰达做了更简单的事情。理想情况下,该解决方案将允许我以某种方式组合注入器函数,因此解决更深层次的问题将是可选的。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-04-25 14:28:50

Ramda是在ES5时代编写的,需要支持ES3。自从引入ES6以来,Ramda确实对旧JS进行了许多改进,现在在普通JS中更容易了。请注意,我是Ramda的创始人和狂热粉丝,但是破坏、箭头函数、默认参数、模板字符串和许多其他东西使得跳过Ramda变得更干净。

下面是一个简单的JS解决方案:

代码语言:javascript
复制
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属性。)

这比您的请求更进一步,省略外键并将其替换为外来对象。它只是基于对你想要的东西的误解,或者认为这是我想要的( ;-)。结果如下:

代码语言:javascript
复制
[
  {
    name: "user 1",
    team: {
      id: "team-1",
      name: "Team 1",
      region: {
        id: "region-1",
        name: "Region 1"
      }
    }
  }, /*... */
]

如果您想保留这些外键,代码会更简单一些:

代码语言:javascript
复制
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参数中,所有这些字符串的含义可能是模糊的。如果是这样的话,您可以用以下内容替换它:

代码语言:javascript
复制
const config = [
  {key: 'teams', foreignKey: 'regionId', target: 'regions', targetId: 'id', newKey: 'region'},
  {key: 'users', foreignKey: 'teamId', target: 'teams', targetId: 'id', newKey: 'team'},
]

只需将reduce的第一行更改为

代码语言:javascript
复制
    (s, {key, foreignKey, target, targetId, newKey}) => ({

(这只是从[ ... ]{ ... }的一个变化。)

票数 1
EN

Stack Overflow用户

发布于 2019-04-24 22:57:52

我使用R.converge提取users并创建teamsregions的查找,然后通过从查找中替换teamId来映射users,并在内部对区域进行同样的操作。

代码语言:javascript
复制
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))
代码语言:javascript
复制
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>

票数 2
EN

Stack Overflow用户

发布于 2019-04-24 22:59:36

下面是使用民间传说/也许的一种可能的方法。我要从高级函数makeUser开始,然后从这里开始-

代码语言:javascript
复制
const makeUser = (user = {}) =>
  injectTeam (user)
    .chain
      ( u =>
          injectRegion (u.team)
            .map
              ( t =>
                  ({ ...u, team: t })
              )
      )
    .getOrElse (user)

每个injector都接受一个get函数、一个set函数和store -

代码语言:javascript
复制
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 -

代码语言:javascript
复制
const injector = (get, set, store = []) => (o = {}) =>
  find (get (o), store) .map (set (o))

现在我们实现find,以便它返回一个可能-

代码语言:javascript
复制
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中的每个项目-

代码语言:javascript
复制
store.users .map (makeUser)

输出

代码语言:javascript
复制
[ { 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"
                    }
          }
  }
]
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55835963

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档