在c#中从数组中提取半相似项

时间:2010-08-23 14:11:52

标签: c# arrays duplicate-removal

这是我的问题

我有以下数组(例如)

string[] arr = new[] { "s_0001", "s_0002", "s_0003", "sa_0004", "sa_0005", "sab_0006", "sab_0007" };

我想做一些提供以下输出的内容

s_0001
sa_0004
sab_0006

我已经尝试了一切,但没有运气!这将是长期项目的第一步,任何帮助都将受到最高的赞赏。

[edit]我不知道这些字母何时会改变,但我知道总会有一个下划线将这些字母与数字分开。我需要以某种方式提取这些字母,然后摆脱重复的

[edit]更具体地说..我想在下划线之前有每个字符串的唯一条目,我不关心的数字

[编辑] 好,朋友们!你真的很活跃我给你。我没想到我会得到如此快速的答案。但是看起来(因为我在过去的8个小时里一直在研究这个问题)我提出了错误的问题

这是我的代码

//Loop through the XML files in the Directory and get
//the objectName and GUID of each file
string[] arr_xmlFiles = Directory.GetFiles(Dir, "*.xml");   //Array with all XML Files in the Directory

foreach (string xmlFile in arr_xmlFiles)
{
    try
    {
        //Get the XMLs Name
        XDocument xmlF = XDocument.Load(xmlFile);
        string objectName = xmlF.Root.Name.ToString();

        //Get the XMLs GUID
        XElement oDcElement = xmlF.Root.FirstNode as XElement;
        Guid oGuid = new Guid(oDcElement.Attribute("DataclassId").Value);

        //Prints out the results 
        Console.WriteLine(" " + objectName + "    " + oGuid);
    }
    catch (XmlException) { }
}

我基本上做的是以下内容 我获取目录中的所有XML文件(它们包含带有GUID的ObjectName)

CM_Commands [0ee2ab91-4971-4fd3-9752-cf47c8ba4a01].xml    
CM_Commands [1f627f72-ca7b-4b07-8f93-c5750612c209].xml

对不起,破损标志是'['不是'_',但没关系。

现在我将所有这些XML保存在一个数组中,然后我想从这些XML中提取每个的ObjectName和GUID

在我这样做之后,我想对只有一个拥有相同objectName的XML进行一些修改

这就是全部

7 个答案:

答案 0 :(得分:3)

编辑#3:详细注释添加到下面的代码段(请参阅编辑2 下的更新代码)。另请注意,如果要从方法中返回这些属性,则需要使用这些属性设置新类,例如:

public class MyClass 
{
    public string ObjectName { get; set; }
    public string Guid { get; set; }
    public string FileName { get; set; }
}

如果有可用的类,则select语句将从select new { ... }更改为:

/* start of query unchanged ... */
select new MyClass
{
    ObjectName = split[0],
    Guid = split[1],
    FileName = f.FullName
};

使用所有这些代码的方法的返回类型为IEnumerable<MyClass>。您可以使用List<MyClass>轻松将其更改为return results.ToList();

编辑#2:从您的文件名中提取 objectName Guid ,您无需执行所有繁琐的XML工作从内部细节中获取信息。

假设您的objectNameGuid始终以空格分隔,您可以使用以下代码。否则可能需要更多解析(或者,可选地,正则表达式)。

string path = @"C:\Foo\Bar"; // your path goes here
var dirInfo = new DirectoryInfo(path);

// DirectoryInfo.GetFiles() returns an array of FileInfo[]
// FileInfo's Name property gives us the file's name without the full path
// LINQ let statement stores the split result, splitting the filename on spaces
// and dots to get the objectName, and Guid separated from the file extension.
// The "select new" projects the results into an anonymous type with the specified
// properties and respectively assigned values. I stored the fullpath just in case.
var query = from f in dirInfo.GetFiles("*.xml")
            let split = f.Name.Split(new[] { ' ', '.' })
            select new 
            {
                ObjectName = split[0],
                Guid = split[1],
                FileName = f.FullName
            };

// Now that the above query has neatly separated the ObjectName, we use LINQ
// to group by ObjectName (the group key). Multiple files may exist under the same
// key so we then select the First item from each group.
var results = query.GroupBy(o => o.ObjectName)
                   .Select(g => g.First());

// Iterate over the results using the projected property names.
foreach (var item in results)
{
    Console.WriteLine(item.FileName);
    Console.WriteLine("ObjectName: {0} -- Guid {1}", item.ObjectName, item.Guid);
}

这适合您的示例数据,但是如果您预计文件名包含.个字符,则上述内容将会中断。为了纠正这种情况变化:

  1. Split至:let split = f.Name.Split(' ')
  2. Guid:Guid = split[1].Substring(0, split[1].LastIndexOf('.')),
  3. <小时/> 既然你知道总会有一个下划线,你可以尝试这种方法:

    string[] arr = {"s_0001", "s_0002", "s_0003", "sa_0004", "sa_0005", "sab_0006", "sab_0007"};
    
    var query = arr.GroupBy(s => s.Substring(0, s.IndexOf('_')))
                   .Select(g => g.First());
    
    foreach (string s in query)
        Console.WriteLine(s);    // s_0001, sa_0004, sab_0006
    

    这将占用每个组的第一项,因此,除非您的项目已预先排序,否则您可能需要在OrderBySelect投放.Select(g => g.OrderBy(s => s).First());

    编辑:为了响应您的编辑,要获得下划线之前的不同字母(即 s,sa,sab ),您可以使用{{3}方法如下:

    var query = arr.Select(s => s.Substring(0, s.IndexOf('_')))
                   .Distinct();    // s, sa, sab
    

    这将为您提供IEnumerable<string>,您可以使用foreach进行迭代,如前所示。

答案 1 :(得分:2)

Dictionary<string,string> lettersToRecords = new Dictionary<string,string>();
arr.Foreach((record) =>
    {
        string letters = record.Split('_')[0];
        if(!lettersToRecords.Keys.Contains(letters))
        {
            lettersToRecords[letters] = record;
        }
    });

答案 2 :(得分:1)

这是我的第一直觉:

string[] arr = {"s_0001", "s_0002", "s_0003", "sa_0004", "sa_0005", "sab_0006", "sab_0007"};

arr.Select(a => Regex.Match(a,@"([A-Za-z]+)_([0-9]+)").Groups[1].ToString()).Distinct();

答案 3 :(得分:0)

ARR [0] ARR [3] ARR [6]

答案 4 :(得分:0)

您可以对数组中的每个字符串使用string.Split('_')

记住前缀,在用该前缀提取一个单词后,忽略所有具有相同前缀的单词。

如果阵列有特定的顺序,你甚至可以优化一点。

答案 5 :(得分:0)

基本上,数组的每个元素都代表两个值:前缀(“s”,“sa”,“sab”)和后缀(“0001”,“0002”,“0003”,“0004”,“ 0005“,”0006“,”0007“)。

这是一个使用Linq将字符串拆分为前缀和后缀,然后根据前缀将元素分组在一起的示例。最后一步只是迭代分组并输出前缀以及使用该前缀找到的第一个元素的后缀:

string[] arr = new[] { "s_0001", "s_0002", "s_0003", "sa_0004", "sa_0005", "sab_0006", "sab_0007" };

var elementsByPrefix = arr.Select(s =>
{
    int indexOfUnderscore = s.IndexOf('_');
    if (indexOfUnderscore >= 0)
    {
        return new { Prefix = s.Substring(0, indexOfUnderscore), Suffix = s.Substring(indexOfUnderscore + 1, s.Length - (indexOfUnderscore + 1)) };
    }
    else
    {
        return new { Prefix = s, Suffix = string.Empty };
    }
}).GroupBy(item => item.Prefix);

foreach (var element in elementsByPrefix)
{
    Console.WriteLine("{0}_{1}", element.Key, element.First().Suffix);
}

此代码的输出与原始问题不完全匹配,因为这将输出“sab_0006”而不是“sab_0007”,但您没有真正指定输出一个与另一个的规则是什么,所以我我只是假设您想要带有该前缀的第一个元素或带有该前缀的任意元素。

答案 6 :(得分:0)

据我所知,您希望通过元素的前缀来区分集合,所以请执行下一步:

class YourStringComparer : System.Collections.Generic.IEqualityComparer<string[]>
{
    public bool Equals(string[] x, string[] y)
    {
        throw new NotImplementedException(); // not used here
    }

    public int GetHashCode(string[] obj)
    {
        return obj.First().GetHashCode();
    }
}

string[] arr = new[] { "s_0001", "s_0002", "s_0003", "sa_0004", "sa_0005", "sab_0006", "sab_0007" };

var r = arr.Select(s => s.Split('_')).Distinct(new YourStringComparer());
// "s_0001", "sa_0004", "sab_0006"