How can I add an element to an IEnumerable in C# [duplicate] - c#

This question already has answers here:
How can I add an item to a IEnumerable<T> collection?
(17 answers)
Closed 9 years ago.
I have the following code:
var contentTypes =
(
from contentType in this._contentTypeService.GetContentTypes()
select new
{
id = contentType.ContentTypeId,
name = contentType.Name
}
);
How can I add another element to contentTypes with an id of 99 and a name of "All"?
I was trying to use contentTypes.Add(
but that does not seem to be allowed with intellisense.

You can't add to an IEnumerable<T>. IEnumerable<T>s represent sequences that you can iterate over; they do not represent a collection that you can add to. What you can do is concatenate to the end of your sequence though, obtaining a new sequence:
var sequence = contentTypes.Concat(
new[] {
new { id = 99, name = "All" }
}
);
Now, if you iterate over sequence, you'll first see the elements of contentTypes streamed to you, and then the final item will be the appended item new { id = 99, name = "All" }.

You can concatenate your new values to the end of the IEnumerable<>.
var contentTypes =
(
from contentType in new[]{new {ContentTypeId = 1, Name="TEST"}}
select new
{
id = contentType.ContentTypeId,
name = contentType.Name
}
).Concat(new[]{new {id = 99, name="All"}});
The resulting IEnumerable will end with 99/All

If you use contentTypes.ToList() you can then add to that list, however doing so will create a new instance of a collection, so your not actually modifying the source collection.

Try this -
var contentTypes =
(
from contentType in this._contentTypeService.GetContentTypes()
select new
{
id = contentType.ContentTypeId,
name = contentType.Name
}
).ToList();
As you have converted contentTypes to a List, it should allow you to add a new item to it.

First, you cannot use IList.Add on an IEnumerable<T>. So you need to create a new collection.
You are selecting an anonymous type, use Concat to add a fixed anynymous type to your query:
var allTypes = new[]{new { id = 99, name = "All" }}; // creates a fixed anonymous type as `IEnumerable<T>`
var contentTypes = from contentType in this._contentTypeService.GetContentTypes()
select new
{
id = contentType.ContentTypeId,
name = contentType.Name
};
var result = allTypes.Concat(contentTypes).ToList(); // concat

Related

Add items to array (C#)

I got some newCity:
ObservCity newCity = new ObservCity ()
{
id = localCityId,
entries = somecity.ToArray()
};
Now I load saved city:
ObservCity loadOldCity = await myService.loadCity(id);
How to add newCity.entries to loadOldCity.entries?
This don't work
newCity.entries.Concat(loadOldCity.entries);
//save newCity + loadOldCity
myService.saveCity(newCity);
You have not posted any class declarations, so I will take a guess and say entries is either an IEnumerable or an array, based on your current code. I will also assume the whole property is read-only. So you just create a new instance:
var updatedCity = new ObservCity
{
id = localCityId,
entries = newCity.entries.Concat(loadOldCity.entries).ToArray()
};
myService.saveCity(updatedCity);
You can add a range of items with the AddRange() method.
newCity.entries.AddRange(loadOldCity?.entries);

.Net Core, merge two lists, remove invalid entries and keep values in the original list

I have a default list of attributes and in incoming list of attributes. I need to remove any items from the incoming list that do now match the Name in the default list. I have tried many different LINQ queries, but have not been able to accomplish this task.
Default List:
Attributes[] defaultAttributes =
{
new ProfileAttributes() { Name = "FirstName", Include = false, Required = false },
new ProfileAttributes() { Name = "MiddleName", Include = false, Required = false },
new ProfileAttributes() { Name = "HomeCountry", Include = false, Required = false },
...
I want to merge the two lists and remove any items where the Name of the incoming list does not match the default list.
For example in the following remove Favorite color because it is an invalid name and preserve the required values.
Attributes[] incomingAttributes =
{
new ProfileAttributes() { Name = "FavoriteColor", Required = true },
new ProfileAttributes() { Name = "MiddleName", Required = false},
new ProfileAttributes() { Name = "HomeCountry", Required = true },
Most incoming lists will not have "Include" So I need to add that and set it to true if it is in the incoming list, otherwise false. I have done that with the following, but interested if there is a way to combine this with the merge.
Revised, I used the following solution:
I used lists instead of array lists. I found this easier to loop through and bind to checkboxes on the form
Attributes[] defaultAttributes
to
List<ProfileAttributes> defaultAttributes = new List<ProfileAttributes>()
Inside the loop for my form:
<input type="checkbox"for="myModel.ProfileAttributes[i].Include"
I created an empty list:
List<ProfileAttributes> validListAttributes = new();
Then I created a loop. If the name is valid add it to the empty list and add the Include attribute:
foreach (var a in myModel.ProfileAttributes) //incomingAttributes
{
if (defaultAttributes.Any(d => d.Name == a.Name))
{
a.Include = true;
validListAttributes.Add(a);
}
}
Then another loop to add missing attributes because all attributes must be display on the form:
foreach (var d in defaultAttributes)
{
if (!validListAttributes.Any(v => v.Name == d.Name))
{
validListAttributes.Add(d);
}
}
Then update the model with the valid list containing all attributes:
myModel.ProfileAttributes = validListAttributes.ToList();
This will be a lot easier with a generic IEqualityComparer whose job is to compare the Name property of the instances involved in the process.
So let's define an IEqualityComparer for the Attributes class
public class ProfileAttributesComparer : IEqualityComparer<ProfileAttributes>
{
public bool Equals(ProfileAttributes obj1, ProfileAttributes obj2)
{
if(obj1 == null && obj2 == null)
return true;
if(obj1 == null || obj2 == null)
return false;
var result = string.Compare(obj1.Name, obj2.Name,
StringComparison.CurrentCultureIgnoreCase);
return result == 0;
}
public int GetHashCode(ProfileAttributes obj)
{
return obj.Name.GetHashCode();
}
}
Now you can process the elements in the incomingAttributes that have a name equal to an instance inside the defaultAttributes and change the property Include to true
var result = incomingAttributes
.Where(x => defaultAttributes.Contains(x, new ProfileAttributesComparer()))
.Select(p => { p.Include = true; return p;});
The result variable contains only the items with the same name but the incomingAttributes list still has three items and two of them have the Include property changed to true.

Using linq with string split

workareaRefs is a string of random values splitted by comma i.e. 4,7,1,7 etc.
I am setting properties to TrackDataFilter and would like to set the Workareas
which is of type IList with the values in workareaRefs var.
So Workareas should contain the values in workareaRefs stored in the variable named r.
Can anyone help me achieve this?
var workareasRefs = workareaRefs.Split(',');
var r = new TrackDataFilter
{
DatePreset = preset,
Workareas = new List<TrackFilterGenericRef>
{
new TrackFilterGenericRef
{
Ref = 2, Type = Enums.ContentTypes.Workarea
}
},
};
Well, I am not sure If I understand your question correctly, so by guessing a bit, I would assume you want to do the following
WorkAreas = new List(workareasRefs);

Given a list of several of the same object, group and combine them based on field value

Sorry for the incoherent title. I don't know how to concisely explain my problem, which is why I didn't really know how to look it up. I'll explain using an example...
Let's say I have a class:
public class cas
{
public string name { get; set; }
public int num { get; set; }
}
With that class, I make several objects and stick them into a list. For the sake of example, I will make 4:
var list = new List<cas>
{
new cas { name = "firstname", num = 1 },
new cas { name = "firstname", num = 2 },
new cas { name = "lastname", num = 3 },
new cas { name = "lastname", num = 4 }
};
Is there a way to take this List and combine any objects with the same name field?
So, the new list would be 1 object with:
name = "firstname", num = 3,
name = "lastname", num = 7
There's the obvious "long" way to do it, but it would be clunky and expensive (go through the list several times to find like-objects). I was wondering if I was missing any clean way of doing it. I intentionally made a simple example so that the answer would be a proof of concept rather than writing my code for me. My actual problem is more complex than this, but I can't figure out this one aspect of it.
Using Linq, you have a GroupBy Method and a Select Method:
list = list.GroupBy(x=> x.name)
.Select(x=> new cas() { name = x.Key, num = x.Sum(y=> y.num) }).ToList();
Or using Elegant query-syntax:
list = (from item in list
group item by item.name into grouping
select new cas()
{
name = grouping.Key,
num = grouping.Sum(x => x.num)
}).ToList();
Note that to use these methods, you have to add using System.Linq at the top of your source file.
You can use linq, you would have to group them on name property and then sum on the num property of each group like:
var result = list.GroupBy(x=>x.name)
.Select(g=> new cas
{
name = g.Key,
num = g.Sum(x=>x.num)
});

LINQ and creating NON anonymous return values

I think I understand returning records of an anonymous type from But in this I want to create NEW CatalogEntries, and set them from the values selected. (context is a Devart LinqConnect database context, which lets me grab a view).
My solution works, but it seems clumsy. I want to do this in one from statement.
var query = from it in context.Viewbostons
select it;
foreach (GPLContext.Viewboston item in query)
{
CatalogEntry card = new CatalogEntry();
card.idx = item.Idx;
card.product = item.Product;
card.size = (long)item.SizeBytes;
card.date = item.Date.ToString();
card.type = item.Type;
card.classification = item.Classification;
card.distributor = item.Distributor;
card.egplDate = item.EgplDate.ToString();
card.classificationVal = (int)item.ClassificationInt;
card.handling = item.Handling;
card.creator = item.Creator;
card.datum = item.Datum;
card.elevation = (int)item.ElevationFt;
card.description = item.Description;
card.dirLocation = item.DoLocation;
card.bbox = item.Bbox;
card.uniqID = item.UniqId;
values.Add(card);
}
CatalogResults response = new CatalogResults();
I just tried this:
var query2 = from item in context.Viewbostons
select new CatalogResults
{ item.Idx,
item.Product,
(long)item.SizeBytes,
item.Date.ToString(),
item.Type,
item.Classification,
item.Distributor,
item.EgplDate.ToString(),
(int)item.ClassificationInt,
item.Handling,
item.Creator,
item.Datum,
(int)item.ElevationFt,
item.Description,
item.DoLocation,
item.Bbox,
item.UniqId
};
But I get the following error:
Error 79 Cannot initialize type 'CatalogService.CatalogResults' with a
collection initializer because it does not implement
'System.Collections.IEnumerable' C:\Users\ysg4206\Documents\Visual
Studio
2010\Projects\CatalogService\CatalogService\CatalogService.svc.cs 91 25 CatalogService
I should tell you what the definition of the CatalogResults is that I want to return:
[DataContract]
public class CatalogResults
{
CatalogEntry[] _results;
[DataMember]
public CatalogEntry[] results
{
get { return _results; }
set { _results = value; }
}
}
My mind is dull today, apologies to all. You are being helpful. The end result is going to be serialized by WCF to a JSON structure, I need the array wrapped in a object with some information about size, etc.
Since .NET 3.0 you can use object initializer like shown below:
var catalogResults = new CatalogResults
{
results = context.Viewbostons
.Select(it => new CatalogEntry
{
idx = it.Idx,
product = it.Product,
...
})
.ToArray()
};
So if this is only one place where you are using CatalogEntry property setters - make all properties read-only so CatalogEntry will be immutable.
MSDN, Object initializer:
Object initializers let you assign values to any accessible fields or properties of an
object at creation time without having to explicitly invoke a constructor.
The trick here is to create a IQueryable, and then take the FirstOrDefault() value as your response (if you want a single response) or ToArray() (if you want an array). The error you are getting (Error 79 Cannot initialize type 'CatalogService.CatalogResults' with a collection initializer because it does not implement 'System.Collections.IEnumerable') is because you're trying to create an IEnumerable within the CatalogEntry object (by referencing the item variable).
var response = (from item in context.Viewbostons
select new CatalogEntry()
{
idx = item.Idx,
product = item.Product,
size = (long)item.SizeBytes,
...
}).ToArray();
You don't have to create anonymous types in a Linq select. You can specify your real type.
var query = context.Viewbostons.Select( it =>
new CatalogEntry
{
idx = it.idx,
... etc
});
This should work:
var query = from it in context.Viewbostons
select new CatalogEntry()
{
// ...
};

Categories