比较顺序无关紧要的字符串列表列表



我的CheckOrder()方法背后的思想是通过查看第一个List是否包含来自第二个List的List来比较两个字符串列表的List。如果是,则从第二个List中删除该List,这样就不能再次对其进行比较。它重复此操作,直到第二个List中没有更多的List可供比较,或者它确定列表的列表不相等。

为了说明我想要的结果是什么,下面是我的代码示例。给定顶部的两个字符串列表的列表,我希望CheckOrder()返回true。

private List<List<string>> testOrder = new List<List<string>> { new List<string> { "Burger", "Cheese" }, new List<string> { "Hot Dog", "Ketchup" } };
private List<List<string>> testOrder2 = new List<List<string>> { new List<string> { "Hot Dog", "Ketchup" }, new List<string> { "Burger", "Cheese" } };
public bool CheckOrder() {
if (testOrder2.Count != testOrder.Count) {
return false;
}
for (int i = 0; i < testOrder.Count; i++) {
if (!testOrder2.Contains(testOrder[i])) {
return false; //here
} else {
testOrder2.Remove(testOrder[i]);
}
}
return true;
}

无论我尝试什么,我不能得到CheckOrder()返回任何东西,但从注释行false。我看到其他建议使用Linq库中的SequenceEqual()之类的东西,但顺序不重要是我试图编写的这个方法的全部要点。

所以你要做一个IEqualityComparer<List<string>>。我将其推广到IEqualityComparer<IList<T>>,这样任何类型的列表都可以使用。我还可以选择允许单个T项值的相等比较器,但这里不是必需的,除非您想使其不区分大小写。

比较器:

public class ListComparer<TItem> : IEqualityComparer<IList<TItem>>
{
private readonly IEqualityComparer<TItem> _itemComparer;
public ListComparer()
: this(EqualityComparer<TItem>.Default)
{
}
public ListComparer(IEqualityComparer<TItem> itemComparer)
{
_itemComparer = itemComparer;
}
// Determine if the two lists have the same content
public bool Equals(IList<TItem> x, IList<TItem> y)
{
if (Object.ReferenceEquals(x, y))
{
return true;
}
else if (x is null != y is null)
{
return false;
}
else if (x is null && y is null)
{
return true;
}
return x.SequenceEqual(y, _itemComparer);
}
// Generate a hashcode from the individual items of the list
// this is dependent on the order of the items
// ({A,B} will produce a different value to {B,A})
public int GetHashCode(IList<TItem> obj)
{
unchecked
{
int hashCode = 63949;
foreach (var item in obj)
{
hashCode += _itemComparer.GetHashCode(item);
hashCode *= 13;
}
return hashCode;
}
}
}

那么我们可以把你的方法简化为:

public bool CheckOrder()
{
// build the comparer for comparing the inner lists
var innerComparer = new ListComparer<string>();
// ensure the lists have the same number of items
if (testOrder2.Count != testOrder.Count)
{
return false;
}
foreach (var order in testOrder)
{
// if an inner list doesn't also exist in testOrder2
// then return false
if (!testOrder2.Contains(order, innerComparer))
{
return false;
}
}
// all lists were found, so return true
return true;
}

正如代码注释中提到的,这假设{ { "Burger", "Hot Dog" } }{ { "Hot Dog", "Burger" } }应该返回false,因为内部列表不同。

这并不能完全处理重复,因为你可以在testOrder中有{ { "Burger", "Hot Dog" }, { "Burger", "Hot Dog" } },在testOrder2中有{ { "Burger", "Hot Dog" }, { "Chicken", "Coke" } },它仍然可以工作。

或者,您可以使用LINQ对数据进行分组和连接,LINQ也会检查计数:

public bool CheckOrder()
{
var innerComparer = new ListComparer<string>();
if (testOrder2.Count != testOrder.Count)
{
return false;
}
return testOrder
// group using the list as the key
.GroupBy(o => o, innerComparer)
// join the other list (which has been grouped by its lists)
.Join(
testOrder2.GroupBy(o => o, innerComparer),
// use the group key from the first list's groups
g1 => g1.Key,
// use the group key from the second list's groups
g2 => g2.Key,
// ensure that the counts are equal and produce a bool as a result
(g1, g2) => g1.Count() == g2.Count(),
// provide the comparer again so that the keys can be checked for equality
innerComparer
)
// ensure that all returned values are true
.All(r => r);
}

如果你想确保只有1对,你可以改变这一行:

(g1, g2) => g1.Count() == g2.Count()

:

(g1, g2) => g1.Count() == 1 && g2.Count() == 1

我认为diplomacywear有更有效的方法,也有一些你应该尝试研究的优点,但我决定尝试一下,这里是一个初级版本,也可以完成这项工作。如果你有任何问题,请告诉我。希望这对你有帮助:)

public class Class1
{
//Renamed testOrder
public List<List<string>> testOrder1 = new List<List<string>> { new List<string> { "Burger", "Cheese" }, new List<string> { "Hot Dog", "Ketchup" } };
public List<List<string>> testOrder2 = new List<List<string>> { new List<string> { "Hot Dog", "Ketchup" }, new List<string> { "Burger", "Cheese" } };

public bool CheckOrder()
{
//Disqualify if counts vary
if (testOrder2.Count != testOrder1.Count)
{
return false;
}
else
{
//This is the janky part, converting the lists to strings ☺
//Create string only working copies of each primary list
List<string> testOrder1c = new List<string>();
List<string> testOrder2c = new List<string>();
//Convert original lists to strings
foreach (var secondaryList in testOrder1)
{
testOrder1c.Add(string.Join(",", secondaryList.ToArray()));
}
foreach (var secondaryList in testOrder2)
{
testOrder2c.Add(string.Join(",", secondaryList.ToArray()));
}
//Create a list of distinct values for each
List<string> testOrder1d = testOrder1c.Distinct().ToList();
List<string> testOrder2d = testOrder2c.Distinct().ToList();
//Disqualify if counts vary
if(testOrder1d.Count != testOrder2d.Count) { return false; }
//Disqualify if distinct lists do not match
testOrder1d.Sort();
testOrder2d.Sort();
if(!testOrder1d.SequenceEqual(testOrder2d))
{
return false;
}
//Create another array to detail the count of each distinct value
List<int> testOrder1dn = new List<int>();
List<int> testOrder2dn = new List<int>();
foreach (var distinctValue in testOrder1d)
{
testOrder1dn.Add(testOrder1c.Where(x => x == distinctValue).Count());
}
foreach (var distinctValue in testOrder2d)
{
testOrder2dn.Add(testOrder2c.Where(x => x == distinctValue).Count());
}
//Finally, we will compare these distinct values and their counts to see if they are equivalent.
//Disqualify if distinct counts do not match
for (int i = 0; i < testOrder1d.Count; i++)
{
if(testOrder1dn[i] != testOrder2dn[i])
{
return false;
}
}
//All distinct lists appear the same number of times in each list:
return true;
}
}

}