我决定在C#中做更多的工作,编写两种查找重复的方法,一种是简单的方法,另一种是利用C#的LINQ功能作为一种学习和培训形式。下面是我的代码,如果有更简洁的方法,我会很感激你对它的风格,设计,如果有更简洁的方法的批评。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Practice
{
#region FindDuplicate
class FindDuplicate
{
private int[] input;
private List<int> dupList;
private IEnumerable<int> dupes;
public FindDuplicate(int[] input)
{
this.input = input;
}
/**
* This is a more advanced version of printing out the duplicates in the sense that we are using
* SQL-Like statements in the form of Group, where, and select. If these were SQL it may look like:
* SELECT * FROM INPUT s1
* JOIN INPUT s2
* ON s2.value = s1.value
*/
public FindDuplicate finderVariantTwo()
{
//Note how we do not need to sort the array, the grouping will do it for us.
this.dupes = this.input.GroupBy(result => result)
.Where(whereValueClause => whereValueClause.Count() > 1)
.Select(indexValue => indexValue.Key);
return this;
}
/**
* This is the "naive" variant, and the one that first came to my mind. The idea here is simple:
* First,
*/
public FindDuplicate getDuplicates()
{
this.dupList = new List<int>();
//for this, let us first sort the array to make it easier for the loop.
Array.Sort(this.input);
for(int i = 0; i < input.Length; i++)
{
//this is to make sure we do not have an index out of bounds.
int next = (i < input.Length-1) ? i+1 : input.Length - 1;
if(input[i] == input[next] && !dupList.Contains(input[i]))
{
dupList.Add(input[i]);
}
}
return this;
}
public void getDuplicatesTwo()
{
String result = "Result => {";
foreach (int i in this.dupes)
{
result += i + ",";
}
result += "}";
Console.WriteLine(result);
}
public void printResultVariantOne()
{
String result = "Result => {";
foreach(int i in this.dupList)
{
result += i + ",";
}
result += "}";
Console.WriteLine(result);
}
#endregion
}
class TestFindDuplicates
{
static void Main(string[] args)
{
int[] input = { 2, 3, 2, 4, 5, 5, 1, 10, 3, 10, 9, 9 };
FindDuplicate find = new FindDuplicate(input);
find.getDuplicates().printResultVariantOne();
Console.WriteLine("\n");
find.getDuplicatesTwo().printResultVariantTwo();
//to stop the window from closing as soon as it finishes running
Console.ReadKey();
}
}
}但有一件事我确实想知道,我知道这不是问题/答案的一面,所以我很抱歉。但是,是否有可能以某种方式将我的输入转换为泛型呢?我试着环顾网络,但没有一个答案是足够的。也就是说,我是否可以说输入是T类型的,而不是硬编码的int值?
发布于 2017-02-07 08:47:39
类FindDuplicate
类名应该是名词,例如DuplicateFinder。
公众FindDuplicate finderVariantTwo() public FindDuplicate getDuplicates()
这不是一个用户友好的API。首先需要创建FindDuplicate类的实例,然后可以搜索副本。这些方法可以返回结果。
如果您想要实现多个搜索重复项的算法,您应该查看一下战略模式。
This.input.GroupBy(结果=> result) .Where(whereValueClause => whereValueClause.Count() > 1) .Select(indexValue => indexValue.Key);
长而冗长的名字是一件好事,但在这里却不是。result是什么?whereValueClause或indexValue是什么?它们更令人困惑,而不是有用。
如果没有更好的名称,则泛型项目大多类似于x,例如其类型名称的首字母(S)。
在Where中,您使用一个组,这样字母g就可以了。同样的情况也适用于Select,您可以在其中使用组agian。
考虑到这一点:
input
.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(g => g.Key);#区域FindDuplicate
区域主要是品味的问题。我不喜欢它们,因为你有更小的+来膨胀/崩溃。我发现如果您开始使用区域,那么您的类/方法/循环等就太大了,您应该开始考虑重构它。有些人使用它们来分组类似的方法,甚至字段/属性。我建议在有一个很好的理由(通常没有,并且需要一个新的类/文件/命名空间)的情况下,尽量少地使用它们。
(我不知道这个部分会让我输多少票)。
你问
但是,是否有可能以某种方式将我的输入转换为泛型呢?
是的。您需要使该方法成为通用的,这意味着应该接受任何集合IEnumerable<T>和实现IEqualityComparer<T>接口的比较器,这样它就可以判断两个项是否相同。
以下是一个例子。
第一个使用GroupBy
public static IEnumerable<T> FindDuplicates<T>(this IEnumerable<T> values, IEqualityComparer<T> comparer)
{
return
values
.GroupBy(x => x, comparer)
.Where(g => g.Count() > 1)
.Select(g => g.Key);
}对于简单的情况,可以使用默认的比较器,如
EqualityComparer<T>.Default对于更复杂的类型,如Person
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}您将编写一个实现IEqualityComparer<T>接口的类:
class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
return x.FirstName == y.FirstName && x.LastName == y.LastName;
}
public int GetHashCode(Person obj)
{
return $"{obj.LastName}, {obj.FirstName}".GetHashCode();
}
}这实际上只是一个非常简单的示例,演示了需要实现的两种方法。您可能希望使名称比较大小写不敏感,或修剪名称或计算不同的哈希代码。做这件事有很多方法,但现在你知道如何开始了。
使用C# 7,您可以通过字典很好地实现它:
public static IEnumerable<T> FindDuplicates<T>(this IEnumerable<T> values, IEqualityComparer<T> comparer)
{
var dictionary = new Dictionary<T, int>(comparer);
foreach (var value in values)
{
dictionary[value] = dictionary.TryGetValue(value, out int counter) ? ++counter : 0;
}
return dictionary.Where(x => x.Value > 1).Select(x => x.Key);
}发布于 2017-02-07 00:49:02
我不会为此使用类,它更像是一种扩展方法。LINQ版本看起来不错,您可以将它变成如下所示的扩展方法:
public static T[] FindDuplicates<T>(this IEnumerable<T> source)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
return source.GroupBy(result => result)
.Where(whereValueClause => whereValueClause.Count() > 1)
.Select(indexValue => indexValue.Key).ToArray();
}注意,这里有一个null检查,因为这是一个扩展方法,我们需要确保它对任何使用它的人都能正常工作,当然,如果出现这种情况,也会处理异常。
在您的类中有一些东西可以改进,但是您应该使用这个单一的方法,这会使您的类变得多余,我将不再讨论它。
如果你想在总体上改进的话,给出一个简单的想法:
StringBuilder与string级联有句话吸引了我的眼球:
注意,我们不需要对数组进行排序,分组将为我们完成这一任务。
分组并不意味着排序。
https://codereview.stackexchange.com/questions/154645
复制相似问题