0

I am using the following code to return an IList:

FileName = Path.GetFileName(files[i]);
IList<DataX> QueryListFromFTP = DataX.GetListFromFTP(FileName);
QueryListFromFTP = (IList<DataX>)QueryListFromFTP
    .Select(x => new { x.user_id, x.date, x.application_ID })
    .ToList()
    .Distinct();

However I keep getting this error:

Unable to cast object of type 'd__7a1[<>f__AnonymousType03[System.String,System.String,System.String]]' to type 'System.Collections.Generic.IList`1[DataXLibrary.DataX]'.

What am I doing wrong?

Craig W.
  • 17,838
  • 6
  • 49
  • 82

3 Answers3

0

The following line creates an anonymous type in c# which is not correspondent to the type Datax:

new { x.user_id, x.date, x.application_ID })

You should alter it to something like this:

Select(x => new Datax(){User_id = x.user_id, Date = x.date, Application = x.application_ID })
Maor Veitsman
  • 1,544
  • 9
  • 21
  • I think QueryListFromFTP is already a list or an IQueryable of DataX. Perhaps he needs a List to match his second select. –  Oct 02 '15 at 22:55
  • He is casting it to a list of Datax so I assumed this was the data structure he was aiming for. – Maor Veitsman Oct 02 '15 at 23:02
  • It works but I need to select Distinct values from this list user_id / date / application_ID 7G5 / 20/09/2015 / 21522080 7G5 / 20/09/2015 / 21522080 vEP / 20/09/2015 / 21522713 fWk / 20/09/2015 / 21523040 and when I try it gives me all the elements, including repeated – Luis Espinoza Oct 02 '15 at 23:14
  • Try the answer at this question: http://stackoverflow.com/questions/14321013/distinct-in-linq-based-on-only-one-field-of-the-table – Maor Veitsman Oct 02 '15 at 23:17
  • Louis, you don't need to group. Select the columns you want to Distinct() on and make sure you Distinct() last i.e. var myList = (QueryListFromFTP.Select(x => new {A=... B=...}).Distinct().ToList(). Note the parenthesis in the previous statement. –  Oct 02 '15 at 23:33
  • Why on earth would you make a `new DataX()` after just tearing one apart? That's a terrible suggestion. – ErikE Oct 03 '15 at 00:06
  • @ErikE If your comment is in response to mine above, plz note I did not suggest he make a new DataX. I said "Select the colums you want..." and my code example implies an anonymous type. –  Oct 03 '15 at 00:18
  • Yes, just noticed it is absolutely redundant :) – Maor Veitsman Oct 03 '15 at 09:36
0

If what you want is a List < DataX > than all you need is:

IList<DataX> QueryListFromFTP = DataX.GetListFromFTP(FileName).Distinct().ToList();
// Use QueryListFromFTP here.

If you want a List of a different type of object as a result of your .Select, than you need to store the result in a List of object of that type i.e. anonymous if that's what you want.

0

There are two problems in your code:

  1. You're converting the List of DataX objects to an "anonymous type object" (the new { x.user_id, x.date, x.application_ID }). This object is not the same type as DataX, and it can't be coerced back to a DataX object automatically.

  2. Trying to read between the lines a little, it looks like you want a distinct list of DataX objects, where distinctness is determined by a subset of the properties of a DataX object. So you have to answer the question, what will you do with duplicates (by this definition) that have different data in other properties? You have to discard some of them. Distinct() is not the right tool for this, because it only applies to the entire object of the IEnumerable it is applied to.

It's almost like you need a DistinctBy with one parameter giving the properties to calculate distinctness with, and a second parameter giving some logic for deciding which of the non-distinct "duplicates" to select. But this can be achieved with multiple IEnumerable methods: GroupBy and a further expression to select an appropriate single itemd from each resulting group. Here's one possible solution:

FileName = Path.GetFileName(files[i]);
IList<DataX> QueryListFromFTP = DataX.GetListFromFTP(FileName)
   .GroupBy(datax => new { datax.user_id, datax.date, datax.application_ID })
   .Select(g => g.First()); // or another expression to choose one item per group
   .ToList();

If, for example, there were a version field and you wanted the most recent one for each "duplicate", you could:

.Select(g => g.OrderByDescending(datax => data.version).First())

Please note, however, that if you just want distinctness over all the properties of the object, and there is no need to select one particular value (in order to get its additional properties after throwing away some objects considered duplicates), then it may be as simple as this:

IList<DataX> QueryListFromFTP = DataX.GetListFromFTP(FileName)
   .Distinct()
   .ToList();

I would furthermore advise that you use IReadOnlyCollection where possible (that's .ToList().AsReadOnly()) and that, depending on your data, you may want to make the GetListFromFTP function perform the de-duplication/distinctness instead.

To answer any concerns that GroupBy isn't the right answer because it may not perform well enough, here is an alternate way to handle this (though I wholeheartedly disagree with you--until tests prove it's slow, it's a perfectly fine answer).

// in a static helper class of some kind
public static IEnumerable<T> DistinctBy<T, TKey>(
    this IEnumerable<T> source,
    Func<T, TKey> keySelector
) {
   if (source == null) {
      throw new ArgumentNullException("source", "Source enumerable cannot be null.");
   }
   if (keySelector == null) {
      throw new ArgumentNullException("keySelector", "keySelector function cannot be null. To perform a generic distinct, use .Distinct().");
   }
   return DistinctByImpl(source, keySelector);
}

private static IEnumerable<T> DistinctByImpl<T, TKey>(
    this IEnumerable<T> source,
    Func<T, TKey> keySelector
) {
   HashSet<TKey> keys = new HashSet<TKey>();
   return source.Where(s => keys.Add(keySelector(s)));
}

It is used like this:

public class Animal {
   public string Name { get; set; }
   public string AnimalType { get; set; }
   public decimal Weight { get; set; }
}

IEnumerable<Animal> animals = new List<Animal> {
    new Animal { Name = "Fido", AnimalType = "Dog", Weight = 15.0M },
    new Animal { Name = "Trixie", AnimalType = "Dog", Weight = 15.0M },
    new Animal { Name = "Juliet", AnimalType = "Cat", Weight = 12.0M },
    new Animal { Name = "Juliet", AnimalType = "Fish", Weight = 1.0M }
};

var filtered1 = animals.DistinctBy(a => new { a.AnimalType, a.Weight });
/* returns:
Name   Type Weight
Fido   Dog  15.0
Juliet Cat  12.0
Juliet Fish 1.0
*/

var filtered2 = animals.DistinctBy(a => a.Name); // or a simple property

/* returns:
Name   Type Weight
Fido   Dog  15.0
Trixie Dog  15.0
Juliet Cat  12.0
*/
ErikE
  • 48,881
  • 23
  • 151
  • 196
  • I don't think he needs to group although that will certainly work. He just needs to select a list of objects with properties he wants to Distinct() on and make sure runs Distinct() last as indicated in my comment on the other answer. Not to further muddy the waters, but if this gets answered that distinct might be more performant if it happened on the server. But that is a story for another day. –  Oct 02 '15 at 23:40
  • @Sam If his use case deviates from my assumption, then I'd be happy to answer differently given new information! In the meantime, it appeared to me that he can't just perform a `Distinct` because he may want the other properties not involved in the particular meaning of distinctness he's using. I'll update my answer though, to draw this out. – ErikE Oct 03 '15 at 00:01
  • @LuisEspinoza Please see my update and the comments. Additionally, if my answer helped you, please click the check mark to the left of it so that I can get credit for doing so. – ErikE Oct 03 '15 at 00:04
  • >>then it may be as simple as this..... Yep that is my answer too LOL. I am interested to know why you suggest AsReadOnly?? thx. –  Oct 03 '15 at 00:16
  • You shouldn't pass around mutable objects unless the intent is for them to be changed. `IReadOnlyCollection` is how you ensure that they can't be modified. About the answer being the same: I wouldn't have given that answer because you already did. However, I believed that you were not barking up the right tree, thus why I gave a different answer. But now my answer is even more complete. – ErikE Oct 03 '15 at 00:27