iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 6
0

在前兩幾天的Kata中,多多少少都看到一些LINQ的操作,例如Any, Distinct。今天會在多介紹一些LINQ較常用的操作與其能達到什麼效果。

String average中,可以看到很多方法都在針對陣列做不同的操作,例如CalculateAverage使用for迴圈算總和,或ToNumbers把string轉換int陣列。

public class StringAverage
{
    private readonly Dictionary<string, int> _numberDict = new Dictionary<string, int>()
    {
        {"zero", 0},
        {"one", 1},
        {"two", 2},
        {"three", 3},
        {"four", 4},
        {"five", 5},
        {"six", 6},
        {"seven", 7},
        {"eight", 8},
        {"nine", 9}
    };

    private const int InvalidNumber = -1;
    private const string InvalidStringNumber = "n/a";

    public string AverageString(string stringNumbers)
    {
        var nums = ToNumbers(stringNumbers);
        if (HasInvalidNumber(nums))
        {
            return InvalidStringNumber;
        }

        return ToString(CalculateAverage(nums));
    }

    private int[] ToNumbers(string str)
    {
        var stringNumbers = str.Split(' ');
        var numbers = new int[stringNumbers.Length];
        for (var i = 0; i < stringNumbers.Length; i++)
        {
            numbers[i] = ToNumber(stringNumbers[i]);
        }
        return numbers;
    }

    private bool HasInvalidNumber(int[] numbers)
    {
        foreach (var number in numbers)
        {
            if (number.Equals(InvalidNumber))
                return true;
        }
        return false;
    }

    private int CalculateAverage(int[] numbers)
    {
        var sum = 0;
        foreach (var number in numbers)
        {
            sum += number;
        }
        return sum / numbers.Length;
    }

    private int ToNumber(string stringNumber)
    {
        return _numberDict.ContainsKey(stringNumber) ? _numberDict[stringNumber] : InvalidNumber;
    }

    private string ToString(int number)
    {
        return _numberDict.ContainsValue(number)
            ? _numberDict.First(x => x.Value == number).Key
            : InvalidStringNumber;
    }
}

在HasInvalidNumber(int[] numbers)中,判斷numbers裡面是否存在至少一個InvalidNumber時,可以使用Count()來取代原本邏輯。

return numbers.Count(x => x == InvalidNumber) != 0

但調整成Any()則會讓語意更精準一點。

return numbers.Any(x => x == InvalidNumber);

值得一提的是,另一個常用的操作All(),可以判斷集合中是否全部元素都符合條件,也是一個很常用的操作。

CalculateAverage(int[] numbers)計算numbers的平均時,可以使用Sum()除以number數量。

return numbers.Sum() / numbers.Length;

或者使用Average()並取Floor。

return (int)Math.Floor(numbers.Average());

在ToNumbers(string str)中,把string轉成int陣列時,則可以使用Select()。

return str.Split(' ').Select(x => ToNumber(x)).ToArray()

可以發現原本要三四行才能表達的邏輯,使用LINQ就一行解決。

對於LINQ所包含都是一些對集合常用到的操作,很多語言也有類似的函式庫可以使用。要是使用的語言沒有類似的函式庫,建議可以自己實作,讓之後操作集合時節省時間。除了上面提到的LINQ操作,之後會再搭配其他場景多介紹一些其他的操作。


上一篇
Day 5 使用資料結構的正確姿勢
下一篇
Day 7 if判斷式的使用
系列文
在Kata中尋找Clean Code是否搞錯了什麼30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言