我有一个函数,它使用条件分隔符连接对象数组。
function getSegmentsLabel(segments) {
var separator = '-';
var segmentsLabel = '';
var nextSeparator = '';
_.forEach(segments, function(segment) {
segmentsLabel += nextSeparator + segment.label;
nextSeparator = segment.separatorUsed ? separator : ' ';
});
return segmentsLabel;
}
用途:
var segments = [
{label: 'First', separatorUsed: true},
{label: 'Second', separatorUsed: false},
{label: 'Third', separatorUsed: true},
{label: 'Forth', separatorUsed: true}
];
getSegmentsLabel(segments); // Result: "First-Second Third-Forth"
如何在不改变变量的情况下以纯函数方式编写上述getSegmentsLabel
函数?我们可以使用lodash函数。
答案 0 :(得分:4)
<强>递归强>
或者代替map / reduce / join,你可以使用直接递归 - 这里的好处是我们不会多次迭代通过集合来计算结果 - 哦,程序非常小,所以很容易消化
小心javascript中的堆栈溢出;相关:How do I replace while loops with a functional programming alternative without tail call optimization?
var segments = [
{label: 'First', separatorUsed: true},
{label: 'Second', separatorUsed: false},
{label: 'Third', separatorUsed: true},
{label: 'Forth', separatorUsed: true}
];
const main = ([x,...xs]) =>
x === undefined
? ''
: xs.length === 0
? x.label
: x.label + (x.separatorUsed ? '-' : ' ') + main (xs)
console.log (main (segments))
// First-Second Third-Forth
函数式编程
我们函数的最后一个实现是非常具体的 - 函数式编程不只是关于使用map和reduce,而是关于使有意义的抽象和编写泛型过程可以容易被重用
这个例子故意与原始代码截然不同,希望它能让你以不同的方式思考程序 - 如果这些东西让你感兴趣,作为这篇文章的后续内容,你可以开始阅读{ {3}}
通过这种方式编写我们的程序,我们在泛型文本模块中表示了“带条件分隔符的可连接文本”的概念,可以在任何其他程序中使用 - 编写者可以创建使用Text.make
的文字单元,并使用Text.concat
进行组合
该程序的另一个优点是分隔符是参数控制的
// type Text :: { text :: String, separator :: String }
const Text =
{
// Text.make :: (String × String?) -> Text
make: (text, separator = '') =>
({ type: 'text', text, separator }),
// Text.empty :: Text
empty: () =>
Text.make (''),
// Text.isEmpty :: Text -> Boolean
isEmpty: l =>
l.text === '',
// Text.concat :: (Text × Text) -> Text
concat: (x,y) =>
Text.isEmpty (y)
? x
: Text.make (x.text + x.separator + y.text, y.separator),
// Text.concatAll :: [Text] -> Text
concatAll: ts =>
ts.reduce (Text.concat, Text.empty ())
}
// main :: [Text] -> String
const main = xs =>
Text.concatAll (xs) .text
// data :: [Text]
const data =
[ Text.make ('First', '-'), Text.make ('Second', ' '), Text.make ('Third', '-'), Text.make ('Fourth', '-') ]
console.log (main (data))
// First-Second Third-Fourth
答案 1 :(得分:2)
您可以使用将返回新数组的map()
方法,然后使用join()
来获取该数组的字符串形式。
var segments = [
{label: 'First', separatorUsed: true},
{label: 'Second', separatorUsed: false},
{label: 'Third', separatorUsed: true},
{label: 'Forth', separatorUsed: true}
];
function getSegmentsLabel(segments) {
return segments.map(function(e, i) {
return e.label + (i != segments.length - 1 ? (e.separatorUsed ? '-' : ' ') : '')
}).join('')
}
console.log(getSegmentsLabel(segments));
答案 2 :(得分:1)
你可以使用一个数组作为分隔符,并决定,如果是间隔符,破折号或没有字符串的分隔符。
const separators = [' ', '', '-'];
var getSegmentsLabel = array => array
.map(({ label, separatorUsed }, i, a) =>
label + separators[2 * separatorUsed - (i + 1 === a.length)])
.join('');
var segments = [{ label: 'First', separatorUsed: true }, { label: 'Second', separatorUsed: false }, { label: 'Third', separatorUsed: true }, { label: 'Forth', separatorUsed: true }];
console.log(getSegmentsLabel(segments));
&#13;
答案 3 :(得分:0)
这里我将功能分开:
// buildSeparatedStr returns a function that can be used
// in the reducer, employing a template literal as the returned value
const buildSeparatedStr = (sep) => (p, c, i, a) => {
const separator = !c.separatorUsed || i === a.length - 1 ? ' ' : sep;
return `${p}${c.label}${separator}`;
}
// Accept an array and the buildSeparatedStr function
const getSegmentsLabel = (arr, fn) => arr.reduce(fn, '');
// Pass in the array, and the buildSeparatedStr function with
// the separator
const str = getSegmentsLabel(segments, buildSeparatedStr('-'));
<强> DEMO 强>
答案 4 :(得分:0)
在这种情况下,最好使用reduceRight
代替map
:
const segments = [
{label: 'First', separatorUsed: true},
{label: 'Second', separatorUsed: false},
{label: 'Third', separatorUsed: true},
{label: 'Forth', separatorUsed: true}
];
const getSegmentsLabel = segments =>
segments.slice(0, -1).reduceRight((segmentsLabel, {label, separatorUsed}) =>
label + (separatorUsed ? "-" : " ") + segmentsLabel,
segments[segments.length - 1].label);
console.log(JSON.stringify(getSegmentsLabel(segments)));
&#13;
如您所见,从右到左遍历数组会更好。
这是一个更有效的程序版本,虽然它使用了变异:
const segments = [
{label: 'First', separatorUsed: true},
{label: 'Second', separatorUsed: false},
{label: 'Third', separatorUsed: true},
{label: 'Forth', separatorUsed: true}
];
const reduceRight = (xs, step, base) => {
const x = xs.pop(), result = xs.reduceRight(step, base(x));
return xs.push(x), result;
};
const getSegmentsLabel = segments =>
reduceRight(segments, (segmentsLabel, {label, separatorUsed}) =>
label + (separatorUsed ? "-" : " ") + segmentsLabel,
({label}) => label);
console.log(JSON.stringify(getSegmentsLabel(segments)));
&#13;
它不是纯粹的功能,但如果我们将reduceRight
视为黑盒子,那么您可以以纯粹的功能方式定义getSegmentsLabel
。
答案 5 :(得分:0)
const segments = [
{label: 'First', separatorUsed: true},
{label: 'Second', separatorUsed: false},
{label: 'Third', separatorUsed: true},
{label: 'Forth', separatorUsed: true}
];
const segmentsLabel = segments.reduce((label, segment, i, arr) => {
const separator = (i === arr.length - 1) ? '' : (segment.separatorUsed) ? '-' : ' ';
return label + segment.label + separator;
}, '');
console.log(segmentsLabel);
&#13;