Programing

항목의 순서에 관계없이 동일성을 위해 두 컬렉션을 비교

crosscheck 2020. 6. 8. 20:48
반응형

항목의 순서에 관계없이 동일성을 위해 두 컬렉션을 비교


C #에서 두 컬렉션을 비교하고 싶지만 이것을 효율적으로 구현하는 가장 좋은 방법은 확실하지 않습니다.

Enumerable.SequenceEqual대한 다른 스레드를 읽었 지만 정확히 내가 원하는 것은 아닙니다.

필자의 경우 두 컬렉션 모두 (주문에 관계없이) 동일한 항목을 포함하면 동일합니다.

예:

collection1 = {1, 2, 3, 4};
collection2 = {2, 4, 1, 3};

collection1 == collection2; // true

내가 일반적으로하는 일은 한 컬렉션의 각 항목을 반복하여 다른 컬렉션에 있는지 확인한 다음 다른 컬렉션의 각 항목을 반복하여 첫 번째 컬렉션에 있는지 확인하는 것입니다. (길이를 비교하여 시작합니다).

if (collection1.Count != collection2.Count)
    return false; // the collections are not equal

foreach (Item item in collection1)
{
    if (!collection2.Contains(item))
        return false; // the collections are not equal
}

foreach (Item item in collection2)
{
    if (!collection1.Contains(item))
        return false; // the collections are not equal
}

return true; // the collections are equal

그러나 이것은 완전히 올바른 것은 아니며 두 컬렉션을 동등하게 비교하는 가장 효율적인 방법은 아닐 것입니다.

내가 잘못 생각할 수있는 예는 다음과 같습니다.

collection1 = {1, 2, 3, 3, 4}
collection2 = {1, 2, 2, 3, 4}

내 구현과 동일합니다. 각 항목을 찾은 횟수를 세고 두 컬렉션의 개수가 같은지 확인해야합니까?


예제는 일종의 C # (의사 C #이라고 함)에 있지만 원하는 언어로 대답하면 중요하지 않습니다.

참고 : 예제에서 정수를 단순화하기 위해 사용했지만 참조 유형 객체도 사용할 수 있기를 원합니다 (내용이 아닌 객체의 참조 만 비교되기 때문에 키로 올바르게 작동하지 않습니다).


Microsoft는 이미 테스트 프레임 워크에서 CollectionAssert를 다룬 것으로 나타났습니다.

비고

두 컬렉션은 같은 양이지만 같은 순서로 같은 원소를 가지고 있다면 동등합니다. 요소가 동일한 객체를 참조하는 경우가 아니라 값이 동일한 경우 요소는 동일합니다.

리플렉터를 사용하여 AreEquivalent () 뒤의 코드를 수정하여 해당하는 동등 비교기를 만듭니다. null을 고려하고, IEqualityComparer를 구현하며, 효율성과 엣지 사례 확인이 있기 때문에 기존 답변보다 더 완벽합니다. 게다가, 그것은 Microsoft입니다 :)

public class MultiSetComparer<T> : IEqualityComparer<IEnumerable<T>>
{
    private readonly IEqualityComparer<T> m_comparer;
    public MultiSetComparer(IEqualityComparer<T> comparer = null)
    {
        m_comparer = comparer ?? EqualityComparer<T>.Default;
    }

    public bool Equals(IEnumerable<T> first, IEnumerable<T> second)
    {
        if (first == null)
            return second == null;

        if (second == null)
            return false;

        if (ReferenceEquals(first, second))
            return true;

        if (first is ICollection<T> firstCollection && second is ICollection<T> secondCollection)
        {
            if (firstCollection.Count != secondCollection.Count)
                return false;

            if (firstCollection.Count == 0)
                return true;
        }

        return !HaveMismatchedElement(first, second);
    }

    private bool HaveMismatchedElement(IEnumerable<T> first, IEnumerable<T> second)
    {
        int firstNullCount;
        int secondNullCount;

        var firstElementCounts = GetElementCounts(first, out firstNullCount);
        var secondElementCounts = GetElementCounts(second, out secondNullCount);

        if (firstNullCount != secondNullCount || firstElementCounts.Count != secondElementCounts.Count)
            return true;

        foreach (var kvp in firstElementCounts)
        {
            var firstElementCount = kvp.Value;
            int secondElementCount;
            secondElementCounts.TryGetValue(kvp.Key, out secondElementCount);

            if (firstElementCount != secondElementCount)
                return true;
        }

        return false;
    }

    private Dictionary<T, int> GetElementCounts(IEnumerable<T> enumerable, out int nullCount)
    {
        var dictionary = new Dictionary<T, int>(m_comparer);
        nullCount = 0;

        foreach (T element in enumerable)
        {
            if (element == null)
            {
                nullCount++;
            }
            else
            {
                int num;
                dictionary.TryGetValue(element, out num);
                num++;
                dictionary[element] = num;
            }
        }

        return dictionary;
    }

    public int GetHashCode(IEnumerable<T> enumerable)
    {
        if (enumerable == null) throw new ArgumentNullException(nameof(enumerable));

        int hash = 17;

        foreach (T val in enumerable.OrderBy(x => x))
            hash = hash * 23 + (val?.GetHashCode() ?? 42);

        return hash;
    }
}

샘플 사용법 :

var set = new HashSet<IEnumerable<int>>(new[] {new[]{1,2,3}}, new MultiSetComparer<int>());
Console.WriteLine(set.Contains(new [] {3,2,1})); //true
Console.WriteLine(set.Contains(new [] {1, 2, 3, 3})); //false

또는 두 컬렉션을 직접 비교하려는 경우 :

var comp = new MultiSetComparer<string>();
Console.WriteLine(comp.Equals(new[] {"a","b","c"}, new[] {"a","c","b"})); //true
Console.WriteLine(comp.Equals(new[] {"a","b","c"}, new[] {"a","b"})); //false

마지막으로 선택한 평등 비교기를 사용할 수 있습니다.

var strcomp = new MultiSetComparer<string>(StringComparer.OrdinalIgnoreCase);
Console.WriteLine(strcomp.Equals(new[] {"a", "b"}, new []{"B", "A"})); //true

간단하고 매우 효율적인 솔루션은 두 컬렉션을 정렬 한 다음 동일한 지 비교하는 것입니다.

bool equal = collection1.OrderBy(i => i).SequenceEqual(
                 collection2.OrderBy(i => i));

이 알고리즘은 O (N * logN)이며 위의 솔루션은 O (N ^ 2)입니다.

컬렉션에 특정 속성이있는 경우 더 빠른 솔루션을 구현할 수 있습니다. 예를 들어 두 컬렉션이 모두 해시 세트 인 경우 복제본을 포함 할 수 없습니다. 또한 해시 세트에 일부 요소가 포함되어 있는지 확인하는 것이 매우 빠릅니다. 이 경우 귀하와 유사한 알고리즘이 가장 빠를 것입니다.


사전 "dict"을 만든 다음 첫 번째 컬렉션의 각 멤버에 대해 dict [member] ++;

그런 다음 동일한 방식으로 두 번째 컬렉션을 반복하지만 각 멤버에 대해 dict [member]-를 수행하십시오.

마지막으로 사전의 모든 멤버를 반복하십시오.

    private bool SetEqual (List<int> left, List<int> right) {

        if (left.Count != right.Count)
            return false;

        Dictionary<int, int> dict = new Dictionary<int, int>();

        foreach (int member in left) {
            if (dict.ContainsKey(member) == false)
                dict[member] = 1;
            else
                dict[member]++;
        }

        foreach (int member in right) {
            if (dict.ContainsKey(member) == false)
                return false;
            else
                dict[member]--;
        }

        foreach (KeyValuePair<int, int> kvp in dict) {
            if (kvp.Value != 0)
                return false;
        }

        return true;

    }

편집 : 내가 알 수있는 한 가장 효율적인 알고리즘과 동일한 순서입니다. 이 알고리즘은 사전에서 O (1) 조회를 사용한다고 가정 할 때 O (N)입니다.


이것은 비교 방법 (C #에서)의 일반적인 구현입니다 (D.Jennings의 영향을 크게 받음).

/// <summary>
/// Represents a service used to compare two collections for equality.
/// </summary>
/// <typeparam name="T">The type of the items in the collections.</typeparam>
public class CollectionComparer<T>
{
    /// <summary>
    /// Compares the content of two collections for equality.
    /// </summary>
    /// <param name="foo">The first collection.</param>
    /// <param name="bar">The second collection.</param>
    /// <returns>True if both collections have the same content, false otherwise.</returns>
    public bool Execute(ICollection<T> foo, ICollection<T> bar)
    {
        // Declare a dictionary to count the occurence of the items in the collection
        Dictionary<T, int> itemCounts = new Dictionary<T,int>();

        // Increase the count for each occurence of the item in the first collection
        foreach (T item in foo)
        {
            if (itemCounts.ContainsKey(item))
            {
                itemCounts[item]++;
            }
            else
            {
                itemCounts[item] = 1;
            }
        }

        // Wrap the keys in a searchable list
        List<T> keys = new List<T>(itemCounts.Keys);

        // Decrease the count for each occurence of the item in the second collection
        foreach (T item in bar)
        {
            // Try to find a key for the item
            // The keys of a dictionary are compared by reference, so we have to
            // find the original key that is equivalent to the "item"
            // You may want to override ".Equals" to define what it means for
            // two "T" objects to be equal
            T key = keys.Find(
                delegate(T listKey)
                {
                    return listKey.Equals(item);
                });

            // Check if a key was found
            if(key != null)
            {
                itemCounts[key]--;
            }
            else
            {
                // There was no occurence of this item in the first collection, thus the collections are not equal
                return false;
            }
        }

        // The count of each item should be 0 if the contents of the collections are equal
        foreach (int value in itemCounts.Values)
        {
            if (value != 0)
            {
                return false;
            }
        }

        // The collections are equal
        return true;
    }
}

Hashset을 사용할 수 있습니다 . 상기 봐 SetEquals의 방법.


편집 : 나는 이것이 실제로 세트에서만 작동한다는 것을 알 자마자 깨달았습니다. 복제 항목이있는 컬렉션을 올바르게 처리하지 못할 것입니다. 예를 들어 {1, 1, 2} 및 {2, 2, 1}은이 알고리즘의 관점에서 동일한 것으로 간주됩니다. 그러나 컬렉션이 세트 (또는 그 평등을 그렇게 측정 할 수있는 경우) 인 경우 아래 내용이 유용하기를 바랍니다.

내가 사용하는 솔루션은 다음과 같습니다.

return c1.Count == c2.Count && c1.Intersect(c2).Count() == c1.Count;

Linq는 표지 아래 사전을 수행하므로 O (N)이기도합니다. (컬렉션의 크기가 같지 않으면 O (1)입니다).

Daniel이 제안한 "SetEqual"메소드, Igor가 제안한 OrderBy / SequenceEquals 메소드 및 제안을 사용하여 상태 점검을 수행했습니다. 결과는 다음과 같습니다. Igor의 경우 O (N * LogN), 광산 및 Daniel의 경우 O (N)입니다.

Linq 교차 코드의 단순성이 선호되는 솔루션이라고 생각합니다.

__Test Latency(ms)__
N, SetEquals, OrderBy, Intersect    
1024, 0, 0, 0    
2048, 0, 0, 0    
4096, 31.2468, 0, 0    
8192, 62.4936, 0, 0    
16384, 156.234, 15.6234, 0    
32768, 312.468, 15.6234, 46.8702    
65536, 640.5594, 46.8702, 31.2468    
131072, 1312.3656, 93.7404, 203.1042    
262144, 3765.2394, 187.4808, 187.4808    
524288, 5718.1644, 374.9616, 406.2084    
1048576, 11420.7054, 734.2998, 718.6764    
2097152, 35090.1564, 1515.4698, 1484.223

반복이없고 순서가없는 경우 다음 EqualityComparer를 사용하여 컬렉션을 사전 키로 사용할 수 있습니다.

public class SetComparer<T> : IEqualityComparer<IEnumerable<T>> 
where T:IComparable<T>
{
    public bool Equals(IEnumerable<T> first, IEnumerable<T> second)
    {
        if (first == second)
            return true;
        if ((first == null) || (second == null))
            return false;
        return first.ToHashSet().SetEquals(second);
    }

    public int GetHashCode(IEnumerable<T> enumerable)
    {
        int hash = 17;

        foreach (T val in enumerable.OrderBy(x => x))
            hash = hash * 23 + val.GetHashCode();

        return hash;
    }
}

다음 은 내가 사용한 ToHashSet () 구현입니다. 해시 코드 알고리즘 (존 소총의 방법으로) 유효 자바에서 비롯됩니다.


static bool SetsContainSameElements<T>(IEnumerable<T> set1, IEnumerable<T> set2) {
    var setXOR = new HashSet<T>(set1);
    setXOR.SymmetricExceptWith(set2);
    return (setXOR.Count == 0);
}

솔루션에는 .NET 3.5와 System.Collections.Generic네임 스페이스 가 필요 합니다. MS에 따르면 , SymmetricExceptWithO (N + m) 의 조작, N 첫번째 세트의 요소의 개수를 나타내는 m 번째의 요소의 수를 표현한다. 필요한 경우 언제든지이 함수에 동등 비교자를 추가 할 수 있습니다.


shouldly 를 사용하는 경우 ContainsAll과 함께 ShouldAllBe를 사용할 수 있습니다.

collection1 = {1, 2, 3, 4};
collection2 = {2, 4, 1, 3};

collection1.ShouldAllBe(item=>collection2.Contains(item)); // true

마지막으로 확장을 작성할 수 있습니다.

public static class ShouldlyIEnumerableExtensions
{
    public static void ShouldEquivalentTo<T>(this IEnumerable<T> list, IEnumerable<T> equivalent)
    {
        list.ShouldAllBe(l => equivalent.Contains(l));
    }
}

최신 정보

ShouldBe 메서드 에는 선택적 매개 변수가 있습니다 .

collection1.ShouldBe(collection2, ignoreOrder: true); // true

.Except ()를 사용하지 않는 이유

// Create the IEnumerable data sources.
string[] names1 = System.IO.File.ReadAllLines(@"../../../names1.txt");
string[] names2 = System.IO.File.ReadAllLines(@"../../../names2.txt");
// Create the query. Note that method syntax must be used here.
IEnumerable<string> differenceQuery =   names1.Except(names2);
// Execute the query.
Console.WriteLine("The following lines are in names1.txt but not names2.txt");
foreach (string s in differenceQuery)
     Console.WriteLine(s);

http://msdn.microsoft.com/en-us/library/bb397894.aspx


중복 된 게시물이지만 컬렉션 비교를위한 솔루션을 확인하십시오 . 꽤 간단합니다.

순서에 관계없이 평등 비교를 수행합니다.

var list1 = new[] { "Bill", "Bob", "Sally" };
var list2 = new[] { "Bob", "Bill", "Sally" };
bool isequal = list1.Compare(list2).IsSame;

항목이 추가 / 제거되었는지 확인합니다.

var list1 = new[] { "Billy", "Bob" };
var list2 = new[] { "Bob", "Sally" };
var diff = list1.Compare(list2);
var onlyinlist1 = diff.Removed; //Billy
var onlyinlist2 = diff.Added;   //Sally
var inbothlists = diff.Equal;   //Bob

사전의 어떤 항목이 변경되었는지 확인할 수 있습니다.

var original = new Dictionary<int, string>() { { 1, "a" }, { 2, "b" } };
var changed = new Dictionary<int, string>() { { 1, "aaa" }, { 2, "b" } };
var diff = original.Compare(changed, (x, y) => x.Value == y.Value, (x, y) => x.Value == y.Value);
foreach (var item in diff.Different)
  Console.Write("{0} changed to {1}", item.Key.Value, item.Value.Value);
//Will output: a changed to aaa

원본 게시물은 여기에 있습니다 .


erickson 은 거의 옳습니다. 복제본 수를 맞추기 위해 Bag 을 원합니다 . Java에서는 다음과 같습니다.

(new HashBag(collection1)).equals(new HashBag(collection2))

C #에 내장 Set 구현이 있다고 확신합니다. 나는 그것을 먼저 사용할 것입니다. 성능에 문제가있는 경우 항상 다른 Set 구현을 사용할 수 있지만 동일한 Set 인터페이스를 사용할 수 있습니다.


다음은 누군가에게 유용 할 경우를 대비하여 ohadsc의 답변에 대한 확장 방법 변형입니다.

static public class EnumerableExtensions 
{
    static public bool IsEquivalentTo<T>(this IEnumerable<T> first, IEnumerable<T> second)
    {
        if ((first == null) != (second == null))
            return false;

        if (!object.ReferenceEquals(first, second) && (first != null))
        {
            if (first.Count() != second.Count())
                return false;

            if ((first.Count() != 0) && HaveMismatchedElement<T>(first, second))
                return false;
        }

        return true;
    }

    private static bool HaveMismatchedElement<T>(IEnumerable<T> first, IEnumerable<T> second)
    {
        int firstCount;
        int secondCount;

        var firstElementCounts = GetElementCounts<T>(first, out firstCount);
        var secondElementCounts = GetElementCounts<T>(second, out secondCount);

        if (firstCount != secondCount)
            return true;

        foreach (var kvp in firstElementCounts)
        {
            firstCount = kvp.Value;
            secondElementCounts.TryGetValue(kvp.Key, out secondCount);

            if (firstCount != secondCount)
                return true;
        }

        return false;
    }

    private static Dictionary<T, int> GetElementCounts<T>(IEnumerable<T> enumerable, out int nullCount)
    {
        var dictionary = new Dictionary<T, int>();
        nullCount = 0;

        foreach (T element in enumerable)
        {
            if (element == null)
            {
                nullCount++;
            }
            else
            {
                int num;
                dictionary.TryGetValue(element, out num);
                num++;
                dictionary[element] = num;
            }
        }

        return dictionary;
    }

    static private int GetHashCode<T>(IEnumerable<T> enumerable)
    {
        int hash = 17;

        foreach (T val in enumerable.OrderBy(x => x))
            hash = hash * 23 + val.GetHashCode();

        return hash;
    }
}

이것 보다 개선 된 솔루션 있습니다.

public static bool HasSameElementsAs<T>(
        this IEnumerable<T> first, 
        IEnumerable<T> second, 
        IEqualityComparer<T> comparer = null)
    {
        var firstMap = first
            .GroupBy(x => x, comparer)
            .ToDictionary(x => x.Key, x => x.Count(), comparer);

        var secondMap = second
            .GroupBy(x => x, comparer)
            .ToDictionary(x => x.Key, x => x.Count(), comparer);

        if (firstMap.Keys.Count != secondMap.Keys.Count)
            return false;

        if (firstMap.Keys.Any(k1 => !secondMap.ContainsKey(k1)))
            return false;

        return firstMap.Keys.All(x => firstMap[x] == secondMap[x]);
    }

이 문제에 대한 많은 해결책이 있습니다. 중복에 신경 쓰지 않으면 둘 다 정렬하지 않아도됩니다. 먼저 항목 수가 같은지 확인하십시오. 그런 다음 컬렉션 중 하나를 정렬하십시오. 그런 다음 정렬 된 컬렉션의 두 번째 컬렉션에서 각 항목을 검색합니다. 주어진 항목을 찾지 못하면 중지하고 false를 반환하십시오. 이것의 복잡성 :-첫 번째 컬렉션 정렬 : N Log (N)-두 번째에서 첫 번째로 각 항목 검색 : NLOG (N)이므로 2 * N * LOG (N)로 일치하고 모든 것을 찾는다고 가정합니다. 이것은 두 가지 정렬의 복잡성과 유사합니다. 또한 차이가있는 경우 더 일찍 중지 할 수있는 이점이 있습니다. 그러나이 비교를 시작하기 전에 둘 다 정렬되고 qsort와 같은 것을 사용하여 정렬을 시도하면 정렬 비용이 더 많이 듭니다. 이에 대한 최적화가 있습니다. 요소의 범위를 알고있는 소규모 컬렉션에 유용한 또 다른 대안은 비트 마스크 인덱스를 사용하는 것입니다. 이렇게하면 O (n) 성능이 제공됩니다. 또 다른 대안은 해시를 사용하여 찾아 보는 것입니다. 소규모 컬렉션의 경우 일반적으로 정렬 또는 비트 마스크 인덱스를 수행하는 것이 훨씬 좋습니다. 해시 테이블은 지역성이 나쁘다는 단점이 있으므로 염두에 두십시오. 다시, 그것은 당신이 경우에만 중복에 신경 쓰지 마십시오. 중복을 설명하려면 둘 다 정렬하십시오.


대부분의 경우 적합한 답변은 Igor Ostrovsky 중 하나이며 다른 답변은 객체 해시 코드를 기반으로합니다. 그러나 객체에 대한 해시 코드를 생성 할 때 객체 ID 필드 (데이터베이스 엔터티의 경우)와 같은 IMMUTABLE 필드 만 기반으로해야합니다.- Equals 메서드를 재정의 할 때 GetHashCode를 재정의해야하는 이유는 무엇입니까?

즉, 두 컬렉션을 비교하면 다른 항목의 필드가 같지 않더라도 비교 방법에서 결과가 사실 일 수 있습니다. 컬렉션을 깊이 비교하려면 Igor의 방법을 사용하고 IEqualirity를 ​​구현해야합니다.

저와 Mr.Schnider의 의견을 가장 많이 투표 한 게시물에서 읽으십시오.

제임스


IEnumerable<T>(세트가 바람직하지 않거나 가능하지 않은 경우) 에 중복을 허용 하고 "순서를 무시"하면을 사용할 수 있습니다 .GroupBy().

나는 복잡성 측정에 대한 전문가는 아니지만, 초보적인 이해는 이것이 O (n)이어야한다는 것입니다. 나는 O (n ^ 2)와 같은 다른 O (n) 연산 내에서 O (n) 연산을 수행하여 오는 것으로 이해 ListA.Where(a => ListB.Contains(a)).ToList()합니다. ListB의 모든 항목은 ListA의 각 항목과 동일한 지 평가됩니다.

내가 말했듯이, 복잡성에 대한 나의 이해는 제한적이므로 내가 틀렸다면 이것을 수정하십시오.

public static bool IsSameAs<T, TKey>(this IEnumerable<T> source, IEnumerable<T> target, Expression<Func<T, TKey>> keySelectorExpression)
    {
        // check the object
        if (source == null && target == null) return true;
        if (source == null || target == null) return false;

        var sourceList = source.ToList();
        var targetList = target.ToList();

        // check the list count :: { 1,1,1 } != { 1,1,1,1 }
        if (sourceList.Count != targetList.Count) return false;

        var keySelector = keySelectorExpression.Compile();
        var groupedSourceList = sourceList.GroupBy(keySelector).ToList();
        var groupedTargetList = targetList.GroupBy(keySelector).ToList();

        // check that the number of grouptings match :: { 1,1,2,3,4 } != { 1,1,2,3,4,5 }
        var groupCountIsSame = groupedSourceList.Count == groupedTargetList.Count;
        if (!groupCountIsSame) return false;

        // check that the count of each group in source has the same count in target :: for values { 1,1,2,3,4 } & { 1,1,1,2,3,4 }
        // key:count
        // { 1:2, 2:1, 3:1, 4:1 } != { 1:3, 2:1, 3:1, 4:1 }
        var countsMissmatch = groupedSourceList.Any(sourceGroup =>
                                                        {
                                                            var targetGroup = groupedTargetList.Single(y => y.Key.Equals(sourceGroup.Key));
                                                            return sourceGroup.Count() != targetGroup.Count();
                                                        });
        return !countsMissmatch;
    }

이 간단한 솔루션IEnumerable제네릭 형식을 강제 로 구현 IComparable합니다. 의 때문에 OrderBy'의 정의.

그러한 가정을하고 싶지 않지만이 솔루션을 계속 사용하려면 다음 코드를 사용할 수 있습니다.

bool equal = collection1.OrderBy(i => i?.GetHashCode())
   .SequenceEqual(collection2.OrderBy(i => i?.GetHashCode()));

Unit Testing Assertions의 목적을 위해 비교하는 경우, 일부 효율성을 창 밖으로 내 보낸 다음 비교하기 전에 각 목록을 문자열 표현 (csv)으로 간단히 변환하는 것이 좋습니다. 이렇게하면 기본 테스트 어설 션 메시지에 오류 메시지 내의 차이점이 표시됩니다.

용법:

using Microsoft.VisualStudio.TestTools.UnitTesting;

// define collection1, collection2, ...

Assert.Equal(collection1.OrderBy(c=>c).ToCsv(), collection2.OrderBy(c=>c).ToCsv());

도우미 확장 방법 :

public static string ToCsv<T>(
    this IEnumerable<T> values,
    Func<T, string> selector,
    string joinSeparator = ",")
{
    if (selector == null)
    {
        if (typeof(T) == typeof(Int16) ||
            typeof(T) == typeof(Int32) ||
            typeof(T) == typeof(Int64))
        {
            selector = (v) => Convert.ToInt64(v).ToStringInvariant();
        }
        else if (typeof(T) == typeof(decimal))
        {
            selector = (v) => Convert.ToDecimal(v).ToStringInvariant();
        }
        else if (typeof(T) == typeof(float) ||
                typeof(T) == typeof(double))
        {
            selector = (v) => Convert.ToDouble(v).ToString(CultureInfo.InvariantCulture);
        }
        else
        {
            selector = (v) => v.ToString();
        }
    }

    return String.Join(joinSeparator, values.Select(v => selector(v)));
}

참고 URL : https://stackoverflow.com/questions/50098/comparing-two-collections-for-equality-irrespective-of-the-order-of-items-in-the

반응형