簡體   English   中英

如何以功能方式實現數組連接?

[英]How to implement array joins in functional way?

我有一個函數,它使用條件分隔符連接一個對象數組。

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函數。

遞歸

或者代替map / reduce / join,你可以使用直接遞歸 - 這里的好處是我們不會多次迭代通過集合來計算結果 - 哦,程序非常小,所以很容易消化

小心javascript中的堆棧溢出; 相關: 如何在沒有尾調優化的情況下使用函數式編程替換while循環?

 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,它是關於進行有意義的抽象和編寫可以輕松重用的通用過程

這個例子與你的原始代碼有意無異,希望它能讓你以不同的方式思考程序 - 如果這些東西讓你感興趣,作為這篇文章的后續內容,你可以開始閱讀關於monoids的內容

通過這種方式編寫我們的程序,我們在一個通用文本模塊中表示了“帶條件分隔符的可連接文本”的概念,可以在任何其他程序中使用 - 編寫者可以使用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 

您可以使用將返回新數組的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)); 

你可以使用一個數組作為分隔符,並決定,如果是一個spacer,破折號或者沒有字符串的分隔符。

 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)); 

在這里,我將功能分開:

// 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

在這種情況下,最好使用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))); 

如您所見,最好從右到左遍歷數組。


這是一個更高效的程序版本,雖然它使用了變異:

 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))); 

它不是純粹的功能,但如果我們將reduceRight視為黑盒子,那么你可以用純粹的功能方式定義getSegmentsLabel

 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); 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM