【问题标题】:Flattening object and limitting number of nesting展平对象和限制嵌套次数
【发布时间】:2020-11-18 11:21:49
【问题描述】:

我想限制对象内扁平结构的数量。考虑下面的例子。

输入:

const exampleObj = {
  foo: {
    bar: {
      biz: "hello"
    }
  }
};

输出:

{foo_bar_biz: "hello"}

我想设置一个限制器 (limit = 2),以便该函数将停止执行递归函数并返回类似的内容

{foo_bar: "[Object] object"}

这是一个sn-p:

const { useState, useEffect} = React;

const exampleObj = {
  foo: {
    bar: {
      biz: "hello"
    }
  }
};

let limit = 0;

function App() {
  const [state, setState] = useState({});

  useEffect(() => {
    flatten(exampleObj);
  }, []);

  const flatten = (data, parent, result = {}) => {
    for (let key in data) {
      const propName = parent ? parent + "_" + key : key;
      if (typeof data[key] === "object") {
        limit++;
        if (limit <= 1) {
          flatten(data[key], propName, result);
        } else {
          setState(prevState => {
            return {
              ...prevState,
              [propName]:
                typeof data[key] === "object" ? "[Object] object" : data[key]
            };
          });
        }
      } else {
        result[propName] = data[key];
        setState({
          ...state,
          [propName]: data[key]
        });
      }
    }
  };

  console.log(state);

  return (
    <div>
      <p>Start editing to see some magic happen :)</p>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

<div id="root"></div>

我已经为此准备了一个解决方案,但这很麻烦。 https://stackblitz.com/edit/react-u5klvc

【问题讨论】:

  • 请在此处添加相关代码。如果外部链接被删除或修改,问题将没有未来价值。你也可以创建a runnable stack snippet
  • 或者如果访问者由于任何其他原因无法打开链接,比如它被阻止了。
  • 啊抱歉,这个链接应该可以工作stackblitz.com/edit/react-td5w6j?file=src/App.js
  • 这里的反应如何?这可能是一个关于数据结构和改变它们的简单问题?
  • 也许这个例子会对你有所帮助。它将 flattening 从反应部分中分离出来。如果这与您尝试做的事情很接近,请告诉我。也许它需要一个答案。 codesandbox.io/s/gracious-galois-gyc0v?file=/src/App.js

标签: javascript reactjs recursion


【解决方案1】:

这是flatten 的一种可能实现。请注意它是如何与 React 或特定的 React 组件完全分离的。它适用于 任何 JavaScript 对象 -

const snakecase = s =>
  s.join("_")
  
const flatten = (t = {}, n = Infinity, join = snakecase) =>
{ const many = (t, n, path) =>
    n >= 0 && Object(t) === t
      ? Object.entries(t).flatMap(_ => one(_, n - 1, path))
      : [ [ join(path), t ] ]
      
  const one = ([ k, v ], n, path) =>
    many(v, n, [...path, k])
  
  return Object.fromEntries(many(t, n, []))
}

举个例子data -

const data =
  { a1: 11
  , a2: { b1: 21, b2: 22 }
  , a3: { b1: { c1: 311, c2: 312 }
        , b2: { c1: 321, c2: 322, c3: { d1: 3231 } }
        }
  }

1depth 变平了一层 -

flatten(data, 1)
{
  "a1": 11,
  "a2_b1": 21,
  "a2_b2": 22,
  "a3_b1": {
    "c1": 311,
    "c2": 312
  },
  "a3_b2": {
    "c1": 321,
    "c2": 322,
    "c3": {
      "d1": 3231
    }
  }
}

2depth 将两个级别变平 -

flatten(data, 2) // => ...
{
  "a1": 11,
  "a2_b1": 21,
  "a2_b2": 22,
  "a3_b1_c1": 311,
  "a3_b1_c2": 312,
  "a3_b2_c1": 321,
  "a3_b2_c2": 322,
  "a3_b2_c3": {
    "d1": 3231
  }
}

默认depth是无穷大-

flatten(data) // => ...
{
  "a1": 11,
  "a2_b1": 21,
  "a2_b2": 22,
  "a3_b1_c1": 311,
  "a3_b1_c2": 312,
  "a3_b2_c1": 321,
  "a3_b2_c2": 322,
  "a3_b2_c3_d1": 3231
}

默认join是snakecase,但是可以在调用处指定参数-

const camelCase = ([ first = "", ...rest ]) =>
  first + rest.map(upperFirst).join("")

const upperFirst = ([ first = "", ...rest ]) =>
  first.toUpperCase() + rest.join("")

flatten(data, 2, camelCase) // => ...
{
  "a1": 11,
  "a2B1": 21,
  "a2B2": 22,
  "a3B1C1": 311,
  "a3B1C2": 312,
  "a3B2C1": 321,
  "a3B2C2": 322,
  "a3B2C3": {
    "d1": 3231
  }
}

展开下面的sn-p,在自己的浏览器中验证结果-

const data =
  { a1: 11
  , a2: { b1: 21, b2: 22 }
  , a3: { b1: { c1: 311, c2: 312 }
        , b2: { c1: 321, c2: 322, c3: { d1: 3231 } }
        }
  }
  
const snakecase = s =>
  s.join("_")
  
const flatten = (t = {}, n = Infinity, join = snakecase) =>
{ const many = (t, n, path) =>
    n >= 0 && Object(t) === t
      ? Object.entries(t).flatMap(_ => one(_, n - 1, path))
      : [ [ join(path), t ] ]
      
  const one = ([ k, v ], n, path) =>
    many(v, n, [...path, k])
  
  return Object.fromEntries(many(t, n, []))
}

const result =
  flatten(data, 2)

console.log(JSON.stringify(result, null, 2))

【讨论】:

  • 哈哈我知道这种感觉。但这不是竞争? 你结合其他泛型的解决方案很好地展示了函数如何相互构建。
  • 不,我并没有真正的竞争力。我们被相同类型的问题所吸引,并且有有趣的混合答案,这些答案彼此非常相似,但又非常不同。事实上,这很有趣。
【解决方案2】:

谢谢你的回答很好。但我认为这已经足够不同了,值得发布。

我将pathgetPaths 等方便的函数保存在我的个人库中。为此,我不得不更改 getPaths 以接受深度参数 (d) 以便更早地转义。

有了这些和Object.fromEntries,我们可以轻松地编写一个partialFlatten 函数来满足我的需要:

const path = (ps = []) => (obj = {}) =>
  ps .reduce ((o, p) => (o || {}) [p], obj)

const getPaths = (obj, d = Infinity) =>
  Object (obj) === obj && d > 0
    ? Object .entries (obj) .flatMap (
        ([k, v]) => getPaths (v, d - 1) .map (p => [Array.isArray(obj) ? Number(k) : k, ...p])
      )
    : [[]]

const partialFlatten = (obj, depth) => 
  Object .fromEntries (
    getPaths (obj, depth) .map (p => [p .join ('_'), path (p) (obj)])
  )

const exampleObj = {foo: {bar: {biz: "hello"}, baz: 'goodbye'}}

console .log ('depth 1:', partialFlatten (exampleObj, 1))
console .log ('depth 2:', partialFlatten (exampleObj, 2))
console .log ('depth 3:', partialFlatten (exampleObj, 3))
.as-console-wrapper {max-height: 100% !important; top: 0}

【讨论】:

    猜你喜欢
    • 2018-10-24
    • 2021-05-14
    • 1970-01-01
    • 2017-07-02
    • 2020-12-22
    • 2021-08-08
    • 1970-01-01
    • 2021-03-22
    • 1970-01-01
    相关资源
    最近更新 更多