过滤 obj[array[@]] 为 nil 或空的数组
Posted
技术标签:
【中文标题】过滤 obj[array[@]] 为 nil 或空的数组【英文标题】:Filter an array where obj[array[@]] is nil or empty 【发布时间】:2021-09-28 10:31:51 【问题描述】:再次练习 Ramda。
所以情况是我有一个obj:
const originalObj =
foo: "bar",
std: "min",
baz: "",
key1: undefined,
key2: "exit",
key3: "val3",
key4: "",
;
我有一个事先知道的数组:
const toCheckArray = ["baz", "key1", "key2", "key3", "key4", "key5"];
对于数组中的每个元素,我需要检查obj中是否存在这样的元素(作为key),以及对应的值是否为nil/empty。如果这样的键存在,并且值是非零/空的,那么我会像这样进行 HTTP 调用来更新值:
const findKey2AndUpdateObj = async (originalObj) =>
const originalKey2 = originalObj.key2;
const key2 = await remoteHttpCall(originalKey2);
return ...originalObj, key2: key2 ;
;
对于数组中的所有元素,远程 HTTP 调用将完全相同,当然除了有效负载。
我的做法是先过滤列表,执行以下步骤:
const hasArray = filter(has(__, originalObj), toCheckArray);
这个我相信会检查目标obj中是否存在作为prop的元素;
我正在尝试将complement(anyPass([isNil, isEmpty]))
应用于 obj 的所有值,然后以某种方式过滤数组中的相应键;
迭代数组?进行 API 调用,然后更新 obj。
我想我的想法并不是最好的方法。很想听听你的想法! 记住 API 调用也很棒!
或者我应该将第 1 步和第 2 步颠倒过来?从 obj 中过滤掉所有的 nil/empty ,然后进行has
检查。
我最终这样做了: filter(has(__, reject(anyPass([isEmpty, isNil]))(obj)), __)(arr)
。但肯定有更好的方法。
干杯!
【问题讨论】:
(不知道 Ramda 是什么......)for (toCheck of toCheckArray) if (originalObj[toCheck]) doStuff()
有什么问题
嗨@Tibrogargan!不,这绝对没有错!而且我猜你会想象我知道用js解决这个问题的普通方法。我正在练习 FP,这个问题用 FP 标记。
因为你在“做 FP”而让事情变得更复杂并不是一个理由。但这是一种 FP 方式,用更多的击键次数和 [可以说] 更少的可读性来做同样的事情:toCheckArray.forEach( toCheck => if (originalObject[toCheck]) doStuff(originalObject[toCheck]) )
你可以做类似intersection(keys(reject(either(isNil, isEmpty))(obj)))(arr)
的事情,但它非常相似
for
绝对有问题。每次写这 3 个字母时,几乎可以肯定地将数据结构逻辑与业务逻辑耦合在一起,从而使重构变得更加困难。请考虑我的实现,其中传递了逻辑,使其更易于共享/重用。
【参考方案1】:
使用管道,您可以使其功能更强大一点,因为您可以将对象传递到管道并输出键,类似于
pipe(reject(either(isNil, isEmpty)),keys,intersection(arr))(obj)
然后您可以将其通过管道传输到 api 调用中(使用 pipeWith
)
【讨论】:
【参考方案2】:我会使用 R.pick 获取仅包含请求键的部分对象,然后使用 R.reject 进一步过滤它们。由于结果是一个对象,您可以使用 R.toPairs,并迭代对进行 api 调用,并用新值重构对象。
const curry, pipe, pick, reject, anyPass, isEmpty, isNil = R;
const fn = curry((arr, obj) => pipe(
pick(arr),
reject(anyPass([isEmpty, isNil])),
)(obj));
const toCheckArray = ["baz", "key1", "key2", "key3", "key4", "key5"];
const originalObj = "foo":"bar","std":"min","baz":"","key2":"exit","key3":"val3","key4":"";
const result = fn(toCheckArray, originalObj);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
【讨论】:
【参考方案3】:函数式编程不一定要使用 Ramda, 有时(例如异步任务),它可能对可读性和声明性代码没有帮助。我会在这里使用简单的递归。
const asyncUpdater = async (keys, updater, data) =>
// base case
if(!keys.length) return data ;
const [head, ...tail] = keys;
const next = (d) => asyncUpdater(tail, updater, d);
const value = data[head];
// continue
if(!value) return next(data);
return next(
...data,
[head]: await updater(head, value)
)
;
// ===
const keys = ["baz", "key1", "key2", "key3", "key4", "key5"];
const data =
foo: "bar",
std: "min",
baz: "",
key1: undefined,
key2: "exit",
key3: "val3",
key4: "",
;
function fakeHttp(key, value)
return Promise.resolve(`$value__REMOTELY_UPDATED__`);
asyncUpdater(keys, fakeHttp, data).then(console.log);
【讨论】:
【参考方案4】:我希望我正确理解了您的要求。干净、自我记录,并且功能与原生 javascript 一样。
const resolveProperties = curry((fn, picklist, input) =>
Promise.resolve(input)
.then(pickAll(picklist))
.then(reject(anyPass([isNil, isEmpty])))
.then(map(fn))
.then(Promise.props)
);
使用和测试用例:
import pickAll, reject, isNil, map, anyPass, isEmpty, curry from "ramda";
import Promise from "bluebird";
const mockHttp = (input) =>
new Promise((res) => setTimeout(res(`$input resolved`), 100));
const resolveProperties = curry((fn, picklist, input) =>
Promise.resolve(input)
.then(pickAll(picklist))
.then(reject(anyPass([isNil, isEmpty])))
.then(map(fn))
.then(Promise.props)
);
test("resolveProperties", () =>
const list = ["baz", "key1", "key2", "key3", "key4", "key5"];
const input =
foo: "bar",
std: "min",
baz: "",
key1: undefined,
key2: "exit",
key3: "val3",
key4: "",
;
return resolveProperties(mockHttp, list, input).then((result) =>
expect(result).toEqual(
key2: "exit resolved",
key3: "val3 resolved",
)
);
);
【讨论】:
Promise.props
是从 bluebird
导入的以上是关于过滤 obj[array[@]] 为 nil 或空的数组的主要内容,如果未能解决你的问题,请参考以下文章
仅当过滤器值不是空字符串、空格或空值时过滤 MySQL 查询最佳实践