1

I have a List<CreditCard>, a property of the credit card is CreditCardNumber, which is a string. Unfortunatly, it can be 1234 1234 1234 1234, or 1234123412341234, which are both the same number.

I need to go through and remove these duplicated items. Could someone assist?

Trevor Pilley
  • 16,156
  • 5
  • 44
  • 60
Craig
  • 18,074
  • 38
  • 147
  • 248
  • how can `1234` be the same like `1234123412341234` ? – gdoron Nov 12 '12 at 22:54
  • @gdoron it's about the spaces – Andrei Nov 12 '12 at 22:55
  • he meant `"1234 1234 1234 1234"` is the same as `"1234123412341234"`. – neeKo Nov 12 '12 at 22:55
  • Is `CreditCard` a class you can modify? – Trevor Pilley Nov 12 '12 at 22:58
  • @Andrei. Well... after someone edit it's clear. – gdoron Nov 12 '12 at 22:59
  • @Craig what about other properties of CreditCard? If we have two credit cards with same number, but other properties will be different - which card to pick? – Sergey Berezovskiy Nov 12 '12 at 23:00
  • 2
    ps. it's probably best to remove all spaces from the string when you assign a value to CreditCardNumber; otherwise you'll incur overhead every time you compare this number, and presumably the spaces are meaningless once in your program. Alternatively you can keep a copy of the original input in one variable, but cache the converted copy in another - tiny memory hit for a small performance gain. – JohnLBevan Nov 12 '12 at 23:07

7 Answers7

5
using System.Linq;

public List<string> GetUniqueCardNumbers(List<string> cardNumbers)
{
    // First replace the spaces with empty strings
    return cardNumbers.Select(cc => cc.Replace(" ", ""))
                      .Distinct()
                      .ToList();
}
Ed Chapel
  • 6,842
  • 3
  • 30
  • 44
2

The best option would be to remove the spaces from the card number on input so you only have to perform the cleaning operation once:

    public class CreditCard: IComparable<CreditCard>
    {
        string creditCardNumberClean;
        string creditCardNumberOriginal;
        public string CreditCardNumber
        {
            get 
            { 
                return this.creditCardNumberOriginal; 
            }
            set
            {
                this.creditCardNumberOriginal = value;
                this.creditCardNumberClean = value.Replace(" ", "");
            }
        }
        public CreditCard(string creditCardNumber)
        {
            this.CreditCardNumber = creditCardNumber;
        }

        public int CompareTo(CreditCard other)
        {
            return this.creditCardNumberClean.CompareTo(other.creditCardNumberClean);
        }
    }

But if that's not possible you basically want to perform a string comparison on the card number just mutating the number to remove all spaces before the comparison:

    class Program
    {
        public static void Main(string[] args) 
        {
            List<string> list = new List<string>(new string[]{"1234 1234 1234 1234", "1234123412341234","9999 9999 9999 9999"});
            SortedSet<string> set = new SortedSet<string>(list, new CreditCardNoComparer());
            foreach (string s in set)
            {
                Console.WriteLine(s);
            }
            Console.ReadKey();
        }
    }
    public class CreditCardNoComparer : IComparer<string>
    {
        public int Compare(string x, string y)
        {
            return x.Replace(" ", "").CompareTo(y.Replace(" ", ""));
        }

    }

EDIT

//version using overridden GetHashCode and Equals methods as per @lazyberezovsky's comments 
public class CreditCard
{
    long creditCardNumberClean; //given the card number is numeric this is the most efficient way of storing it
    string creditCardNumberOriginal;
    public string CreditCardNumber
    {
        get
        {
            return this.creditCardNumberOriginal;
        }
        set
        {
            this.creditCardNumberOriginal = value;
            this.creditCardNumberClean = long.Parse(value.Replace(" ", "")); 
        }
    }
    public CreditCard(string creditCardNumber)
    {
        this.CreditCardNumber = creditCardNumber;
    }
    public override bool Equals(object obj)
    {
        CreditCard other = obj as CreditCard;
        return 
            other == null 
            ? false 
            : this.creditCardNumberClean.Equals(other.creditCardNumberClean);
    }
    public override int GetHashCode()
    {
        return this.creditCardNumberClean.GetHashCode();
    }
}
JohnLBevan
  • 22,735
  • 13
  • 96
  • 178
  • +1 Best solution - get rid of various representation of keys. But I'd probably go with Equals (compare numbers) and GetHashCode (number code). – Sergey Berezovskiy Nov 12 '12 at 23:19
  • Thanks @lazyberezovsky; agreed overriding Equals and GetHashCode's the better approach; I'll update my example. . . – JohnLBevan Nov 12 '12 at 23:40
0
  1. Remove white space from every one
  2. Use Distinct();
Stan
  • 25,744
  • 53
  • 164
  • 242
0

You can create your own EqualityComparer for CreditCard which removes whitespace and use Enumerable.Distinct to remove the duplicates:

public class CardComparer : IEqualityComparer<CreditCard>
{
    private readonly Regex re = new Regex(@"\s+");

    public bool Equals(CreditCard x, CreditCard y)
    {
        return re.Replace(x.Number, "") == re.Replace(y.Number, "");
    }

    public int GetHashCode(CreditCard obj)
    {
        return re.Replace(obj.Number, "").GetHashCode();
    }
}

Then:

IEnumerable<CreditCard> unique = sourceList.Distinct(new CardComparer());
Lee
  • 142,018
  • 20
  • 234
  • 287
0

First you have to determine what constitutes "equal". If it's as simple as removing embedded spaces, then you can use one of the previous solutions that removes the white space and calls Distinct. Otherwise you'll have to create an equality comparer and pass it to the Distinct overload.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
0
IEnumerable<CreditCard> distinctCards = cards
  .Select(card => new {
     card, 
     ccn = card.CreditCardNumber.Replace(" ", string.Empty)
  })
  .GroupBy(x => x.ccn)
  .Select(g => g.First().card) //group has at least 1 member, so First() is safe
  //.ToList()   ???
spender
  • 117,338
  • 33
  • 229
  • 351
0

You can extract only the numbers from any string, in your case you can only make a replace of the space character for a empty character, for a more complex string format you can use the next code for get only numbers. You can check this answer: get only digits

string justNumbers = new String("asd123 f3 4.34-3 ".Where(Char.IsDigit).ToArray());

Then you will have only the digits of your CreditCardNumber and you can search for duplicated values.

Community
  • 1
  • 1
Heybrajham
  • 26
  • 5