Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
907 views
in Technique[技术] by (71.8m points)

javascript - Ramda recursive merge based on keys' match

I have two lists of nodes, who shaped like so:

interface TreeNode {
    data: {
        name: string,
        sharedProp: boolean,
        oldProp: boolean
    },
    children: TreeNode[],
    parents: TreeNode[],
    thereAreSomeShallowProps: any,
}

The full dataset would be an array of TreeNode

What I'd like is to have a function that I can traverse down this tree, merging changes in a changes tree into the base tree. Some of the feature it'd need:

  • Match the values of a specified key (in this case support multilevel keys), and merge the matches
    • possibly with flatten and groupBy
  • When the values of the object are arrays, recurse
  • Resistant to circular references
  • Able to work with very large objects (over 100k nodes, at least)

Some of the functions I've looked at (but am unsure how to string together to build the function I want):

  • applySpec
  • groupBy
  • mergeWithKey
  • mergeDeepWithKey

Here is a sandbox to check out, with some tests that should explain better what I'm trying to achieve

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

While this may not be the best approach, it's one we can build easily with tools we have around the house. (In my case, with ones I wrote in another StackOverflow answer.) I freely used Ramda functions here, as the question was tagged Ramda (disclaimer: I'm a Ramda author), but below I show an alternate version that builds the required utility functions from scratch.

This makes the assumption that your changes object will be and/or will include sparse arrays. If not, how do you plan on matching things up?

Here is my approach:

// Helper or utility functions
function * getPaths(o, p = []) {
  if (Object(o) !== o || Object .keys (o) .length == 0) yield p 
  if (Object(o) === o)
    for (let k of Object .keys (o))
      yield * getPaths (o[k], [... p, Number.isInteger (Number (k)) ? Number (k) : k])
}

const allPaths = (o) => [... getPaths(o)]

// Main function
const applyChanges = (obj, changes) =>
  reduce ((o, p) => assocPath (p, path (p, changes), o), obj, allPaths (changes))

// Sample data
const base = [
  {a: 1, b: {c: 11, d: [{e: 100}, {e: 111}]}},
  {a: 2, b: {c: 22, d: [{e: 200}, {e: 222}]}},
  {a: 3, b: {c: 33, d: [{e: 300}, {e: 333}]}},
]

const deltas = [
  {a: 8, b: {       d: [        , {e: 888}]}},
  ,
  {      b: {c: 99, d: [{e: 999},         ]}},
]

// Demonstration
console .log (
  applyChanges (base, deltas)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script> const {reduce, assocPath, path} = R                 </script>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...