如何使用Linq从字符串数组的特定字符集构建字符串

时间:2015-09-14 12:30:21

标签: c# linq

大家好Linq专家,

我想知道如何解决以下问题:

有一个字符串数组:

string[] words={"a#p#e","#pp#e","a##le"};

如何通过Linq获得“apple”这个词?

非常感谢!

3 个答案:

答案 0 :(得分:2)

words.SelectMany(ar=>ar.Select((c,i)=>new{Index=i, Letter = c}).Where(o=>char.IsLetter(o.Letter))).OrderBy(o=>o.Index).GroupBy(o=>o).Select(g=>g.Key);

最有可能的是,这可以简化,但是,它可以工作(在LinqPad中快速组合在一起)。

这假设您只使用字母(可以修改为使用char.IsLetterOrDigit),或使用Where中的其他过滤。

此外,特别是对于@MarkPeters,一种查询语法,正确命名为version:

from partialWord in words
from characterWithPosition in partialWord.Select((character, position) => new{character,position})
where char.IsLetter(characterWithPosition.character)
orderby characterWithPosition.position
group characterWithPosition by characterWithPosition into  allCharactersWithPositions
let singleCharacterWithPosition = allCharactersWithPositions.Key
select singleCharacterWithPosition.character

答案 1 :(得分:2)

这应该有效:

string[] words = { "a#p#e", "#pp#e", "a##le" };

var apple = words
    .Aggregate(
        (str1, str2) => { return String.Concat(str1.Zip(str2, 
            (c1, c2) => c1 == '#' ? c2 : c1).ToArray());
        });

它的工作原理是检查第一个字符串中是否有任何不需要的字符,并用第二个字符串中的相应字符替换它们。然后将合并的字符串传递给下一个迭代,其中合并下两个字符串。最后返回结果。

答案 2 :(得分:0)

@ mrbraitwistle的答案是正确的解决方案;在IEnumerable<char>.Zip方法

中执行IEnumerable<string>.Aggregate方法

以下答案为@ Gerino的答案部分提供了明确的类型定义,它使用了相当多的anonyomous类型和委托;另外,这个答案使用Linq.ExpressionsIQueryable<T>;

虽然有一个问题,但它位于GetHashCode下面的IndexLetterPair类型中。

我们将创建一个具体类型,而不是声明匿名类型new { Index = i, Letter = c };但是,GroupBy将无法正常运行,除非我们覆盖GetHashCode object方法(我们的类型继承)。我们还将实现IEquatable接口(其中T是另一个IndexLetterPair),此接口将由IQueryable<T>.GroupBy方法使用。

此外,也可以覆盖object.Equal方法 - 尽管由于GetHashCode被覆盖,可能不必这样做。

public class IndexLetterPair IEquatable<IndexLetterPair>
{
    public int Index { get; set; }
    public char Letter { get; set; }

    public override int GetHashCode()
    {
        return new { Index = this.Index, Letter = this.Letter }.GetHashCode();
    }

    // (new IndexLetterPair() as object).Equals(new IndexLetterPair());
    public override bool Equals(object obj)
    {
        return (this.GetHashCode() == obj.GetHashCode());        
    }

    // new IndexLetterPair().Equals(new IndexLetterPair());
    public bool Equals(IndexLetterPair other)
    {
        return (this.GetHashCode() == other.GetHashCode());
    }
}

为了方便,我们将声明一个IQueryable<string>的扩展类和方法,它执行/声明我们定义的表达式的序列。

using Linq.Expressions;
public static class MyQueryableStringExtensions
{
    readonly static Func<char, int, IndexLetterPair> SelectCharIndexPairingClause = (c, i) => new IndexLetterPair() { Index = i, Letter = c };
    readonly static Func<IndexLetterPair, bool> WhereIsLetter = (dyn) => char.IsLetter(dyn.Letter);
    readonly static Expression<Func<string, IEnumerable<IndexLetterPair>>> ManyClause = (arr) => arr.Select(SelectCharIndexPairingClause).Where(WhereIsLetter);
    readonly static Expression<Func<IndexLetterPair, int>> OrderByIndexClause = (pairing) => pairing.Index;
    readonly static Expression<Func<IndexLetterPair, IndexLetterPair>> GroupByClause = (pairing) => pairing;
    // if IEquatable<IndexLetterPair> is implemented ? uses IEquatable<IndexLetterPair>.Equals : (IndexLetterPair() as object).Equals
    readonly static Expression<Func<IGrouping<IndexLetterPair, IndexLetterPair>, IndexLetterPair>> GroupSelectKeyClause = (pairing) => pairing.Key;

    public static IQueryable<char> SelectManyGroupedLetterChars(this IQueryable<string> words)
    {
        return words.SelectMany(ManyClause)
            .OrderBy(OrderByIndexClause)
            .GroupBy(GroupByClause)
            .Select(GroupSelectKeyClause)
            .Select((pair) => pair.Letter);
    }
}
 string[] words = new string[] { "a#p#e", "#pp#e", "a##le" };                        

 IQueryable<string> queryableWords = words.AsQueryable();    
 IQueryable<char> queryGroupedLetterChars = queryableWords.SelectManyGroupedLetterChars();

 // string(char[]);
 string word = new string(queryGroupedLetterChars.ToArray());