使用 Ramda 以声明性方式编写具有优先级的 Boolean -> 字符串映射器

Posted

技术标签:

【中文标题】使用 Ramda 以声明性方式编写具有优先级的 Boolean -> 字符串映射器【英文标题】:Writing a Boolean -> string mapper with precedence in a declarative way using Ramda 【发布时间】:2021-10-30 01:43:42 【问题描述】:

如何以更具功能性/声明性的方式编写此代码?

type FieldType = "dropdown" | "text" | "file" | null;
const getFieldType = (field: isDropdown?: boolean, isTextInput?: boolean, isFileModal?: boolean) => 
// a field can have all 3 of the above boolean properties at once as "true"
//but some of them are more important than others - eg. isDropdown
//hence the if/else if below
if(field.isDropdown) 
  return 'dropdown';
 else if(field.isTextInput) 
  return 'text';

else if (field.isFileModal) 
  return 'file';


我的解决方案:

const getFieldType = (field) => 
  const mapConditionToType = [
    [field.isDropdown, 'dropdown'],
    [field.isTextInput, 'text'],
    [field.isFileModal, 'file']
  ]

  return mapConditionToType.find([condition, type] => condition)?.type ?? null;

如您所见,它根本不包含...任何 Ramda。我想知道是否可以使用函数式编程以更具声明性的方式编写它?

【问题讨论】:

Field 可以同时是isDropdown: trueisTextInput: true 吗? 是的。请参阅我在第一个代码块中的评论“一个字段可以包含所有 3 个 ...”。但你是对的,我没有提到它们都可以是真的:P find 将返回第一次出现,而不是全部。如果所有三个道具都为真,那么预期的行为是什么? 预期行为是返回第一个匹配元素。 “isDropdown”比“isTextInput”等重要。 嗯,R.cond 看起来不错 【参考方案1】:

我建议利用一些声明性库,例如 Ramda

/**
 @typescript ```
   R.cond<Record<string, boolean>, FieldType>([ ... ]);
   ```
**/
const getFieldType = R.cond([
  
  [R.prop('isDropdown'), R.always('dropdown')],
  [R.prop('isTextInput'), R.always('text')],
  [R.prop('isFileModal'), R.always('file')],
  [R.T, R.always(null)],

]);

console.log(
  'it should return "dropdown" =>',
  getFieldType( isDropdown: true ),
)

console.log(
  'it should return "file" =>',
  getFieldType( isFileModal: true ),
)

console.log(
  'it should return "text" =>',
  getFieldType( isTextInput: true ),
)

console.log(
  'it should return "null" =>',
  getFieldType( isMultiSelect: true ),
)
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js" integrity="sha512-3sdB9mAxNh2MIo6YkY05uY1qjkywAlDfCf5u1cSotv6k9CZUSyHVf4BJSpTYgla+YHLaHG8LUpqV7MHctlYzlw==" crossorigin="anonymous" referrerpolicy="no-referrer"&gt;&lt;/script&gt;

【讨论】:

【参考方案2】:

正如@walnut_salami 的评论中所述,cond 是在这种情况下使用的正确运算符。您可以使用更高级的运算符来测试第一个参数中的真实性,但由于被检查的属性返回真实值,所以 prop 运算符应该就足够了。

interface Field  
  isDropdown?: boolean;
  isTextInput?: boolean;
  isFileModal?: boolean;


const getFieldType = cond<Field, FieldType>([
  [prop('isDropdown'), always('dropdown') ],
  [prop('isTextInput'), always('text') ],
  [prop('isFileModal'), always('file') ],
  [T, always(null)]
]);

console.log(getFieldType( isDropdown: true )); // dropdown
console.log(getFieldType( isTextInput: true )); // text
console.log(getFieldType( isFileModal: true )); // file
console.log(getFieldType( )); // null
console.log(getFieldType( isTextInput: true, isDropdown: true )); // dropdown

由于您使用的是 TypesSript,因此您可以利用 cond 的重载,它接受主题类型和返回类型的参数。通过这样做,您和重构为 FieldFieldType 将引发编译器错误,如果它们以某种方式破坏了运算符中正在使用的内容。

【讨论】:

【参考方案3】:

R.cond 适合您希望提供的对数组的结构。您可以使用R.pipe 生成一个接受对数组的函数,然后根据R.cond 生成一个新函数(来自Daniel Gimenez's answer 的测试用例):

const  pipe, map, evolve, prop, always, append, T, cond  = R

const createGetFieldType = pipe(
  map(evolve([prop, always])), // wrap each element in the pairs with the relevant function
  append([T, always(null)]), // add the default value
  cond // partially apply to cond
)

const getFieldType = createGetFieldType([
  ['isDropdown', 'dropdown'],
  ['isTextInput', 'text'],
  ['isFileModal', 'file']
])

console.log(getFieldType( isDropdown: true )); // dropdown
console.log(getFieldType( isTextInput: true )); // text
console.log(getFieldType( isFileModal: true )); // file
console.log(getFieldType( )); // null
console.log(getFieldType( isTextInput: true, isDropdown: true )); // dropdown
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js" integrity="sha512-3sdB9mAxNh2MIo6YkY05uY1qjkywAlDfCf5u1cSotv6k9CZUSyHVf4BJSpTYgla+YHLaHG8LUpqV7MHctlYzlw==" crossorigin="anonymous" referrerpolicy="no-referrer"&gt;&lt;/script&gt;

【讨论】:

以上是关于使用 Ramda 以声明性方式编写具有优先级的 Boolean -> 字符串映射器的主要内容,如果未能解决你的问题,请参考以下文章

Ramda js:具有嵌套对象数组的深度嵌套对象的镜头

如何以视觉语言格式编写具有内在大小优先级的内容?

C语言中结合性自右向左怎么理解

具有 React useState 设置功能的打字稿类型的 Ramda 镜头组合

优先队列比较

优先队列比较