Combine data gathered from different sources into one IEnumerable object - c#

I have this method below that gathers data from different sources and returns it as one IEnumerable.
I'm having trouble though, figuring out how to combine all the sources into one object of type TotalRoomContents.
TotalRoomContents is of type IEnumerable<String>.
Here is the unfinished method:
public static TotalRoomContents GetRoomContents(this Dungeon dungeon)
{
var customArmor = dungeon.Rooms
.Select(pe => pe.Room)
.Where(e => e.Monster.IsActive);
// check each customArmor object to see if it exists in the MapLookupByRoomId dictionary
if (customArmor != null && MapLookupByRoomId.ContainsKey(customArmor.Id))
// add all item(s) of type customArmor to TotalRoomContents()
if(dungeon.RoomType?.InventoryContent != null)
{
// add item(s) from dungeon.RoomType?.InventoryContent to TotalRoomContents()
}
return new TotalRoomContents()
}
As you can see, I don't know how to add the item(s) to the TotalRoomContents object.
The items will be from dungeon.RoomType?.InventoryContent and all the customArmor objects found in the linq query.
Is there a way to do this in one method or do I need to create some type of other method to do this?
Thanks!

You could create a wrapper class that would take care of this. A possible implementation could look like this
public class AggregateEnumerable<T> : IEnumerable<T> {
private readonly IEnumerable<T>[] _sources;
public AggregateEnumerable( params IEnumerable<T>[] sources ) {
_sources = sources;
}
public IEnumerator<T> GetEnumerator() {
foreach( var source in _sources ) {
var enumerator = source.GetEnumerator();
while( enumerator.MoveNext() )
yield return enumerator.Current;
}
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
And then you would use it like
var firstEnumerable = new[] { "Peter", "John" };
var secondEnumerable = new[] { "Thomas", "George" };
var myEnum = new AggregateEnumerable<string>(firstEnumerable, secondEnumerable);
foreach( var value in myEnum )
Console.WriteLine(value);

Why don't you create a list of "RoomContent" (that represents whatever a room can contain), and start adding all the different results from your other queries?
List<RoomContent> TotalRoomContents = new List<RoomContent>();
if (/* Whatever condition needs to be met */)
{
TotalRoomContents.Add(/* Whatever you may want */);
}
Also, you should know that Linq queries are not executed until the code needs to enumerate it, so, basically you can build a query in steps:
// This is just to simulate the data source
IQueryable<RoomContent> query = allPossibleRoomContents.AsQueryable();
query = query.Where(x => x.ContentDescription = "Sword");
query = query.Where(x => x.ContentDescription = "Axe");
// This is where the actual work is done
return query.ToList();
Hope this helps!

Related

Calling Select inside of List<> extended method in C#

Just wondering why a Select call won't execute if it's called inside of an extended method?
Or is it maybe that I'm thinking Select does one thing, while it's purpose is for something different?
Code Example:
var someList = new List<SomeObject>();
int triggerOn = 5;
/* list gets populated*/
someList.MutateList(triggerOn, "Add something", true);
MutateList method declaration:
public static class ListExtension
{
public static IEnumerable<SomeObject> MutateList(this IEnumerable<SomeObject> objects, int triggerOn, string attachment, bool shouldSkip = false)
{
return objects.Select(obj =>
{
if (obj.ID == triggerOn)
{
if (shouldSkip) shouldSkip = false;
else obj.Name += $" {attachment}";
}
return obj;
});
}
}
The solution without Select works. I'm just doing a foreach instead.
I know that the Select method has a summary saying: "Projects each element of a sequence into a new form." But if that were true, then wouldn't my code example be showing errors?
Solution that I used (Inside of the MutateList method):
foreach(SomeObject obj in objects)
{
if (obj.ID == triggerOn)
{
if (shouldSkip) shouldSkip = false;
else obj.Name += $" {attachment}";
}
});
return objects;
Select uses deferred execution, meaning that it does not actually execute until you try to iterate over the results, with a ForEach, or using Linq methods that require the actual results like ToList or Sum.
Also, it returns an iterator, it does not run on the items in-place, but you're not capturing the return value in your calling code.
For those reasons - I would recommend not using Select to mutate the object in the list. You're just wrapping a ForEach call in a less clean way. I would just use ForEach within the method.

Yield item in a list

In my application there is a List<MyItem> with a getter only:
public List<MyItem> myList
{
get
{
MyHost.GetItemFromID(_i1); //this may be a long operation
MyHost.GetItemFromID(_i2);
MyHost.GetItemFromID(_i3);
MyHost.GetItemFromID(_i4);
MyHost.GetItemFromID(_i5);
}
}
This list needs sometimes to be retrieved as whole and other times only certain item has to be accessed: i.e. myList[3]. Is there a way of not building the entire list as I only need the fourth item?
You could wrap the list with a class, say 'myListContainer' and overload its '[]' operator so that you could do something like this:
myListContainer[3]
which will invoke the call
MyHost.GetItemFromID(_i3);
and return the desired list item.
I'll add a full example if needed
EDIT
public class myListContainer
{
public MyItem this[int i]
{
get
{
return MyHost.GetItemFromID(i);
}
}
}
and add method to get the entire list.
You could return IEnumerable<MyItem> and use yield return:
public IEnumerable<MyItem> MyItems
{
get
{
yield return MyHost.GetItemFromID(_i1); //this may be a long operation
yield return MyHost.GetItemFromID(_i2);
yield return MyHost.GetItemFromID(_i3);
yield return MyHost.GetItemFromID(_i4);
yield return MyHost.GetItemFromID(_i5);
}
}
Then it is using deferred execution and you can write:
var fourItems = MyItems.Take(4).ToList();
Note that it might be a good idea to change the order of execution if the order doesn't matter and only the first call of GetItemFromID takes more time than the others:
yield return MyHost.GetItemFromID(_i2);
yield return MyHost.GetItemFromID(_i3);
yield return MyHost.GetItemFromID(_i4);
yield return MyHost.GetItemFromID(_i5);
yield return MyHost.GetItemFromID(_i1); //this may be a long operation
I think i have misuderstood the requirement. I've read "sometimes I only need the fourth item" as "only need four items"
So you could use my aproach but with ElementAt:
MyItem fourthItem = MyItems.ElementAt(3);
If you don't know if there's a fourth use ElementAtOrdefault.
I don't know what type your keys for _i1 etc are, but let's assume they are ints. Then you could do this:
public IEnumerable<MyItem> GetItems(params int[] keys)
{
return keys.Select(key => MyHost.GetItemFromID(key));
}
Which you could call like this:
var myItems = GetItems(_i2, _i3, _i5).ToList();
or
var myItems = GetItems(_i3).ToArray();
and so on.
Note: If you only ever want a list returned, you can do the conversion inside GetItems() itself:
public List<MyItem> GetItems(params int[] keys)
{
return keys.Select(key => MyHost.GetItemFromID(key)).ToList();
}
This approach requires a method rather than a property, though.
Note:
This assumes by '4th item' you mean an ordinal number, not the actual id itself.
Build a map in advance and return only items you wish to.
List<int> map = new List<int>
{
_i1,
_i2,
_i3,
_i4,
_i5,
};
public IEnumerable<MyItem> myList(params int[] indices)
{
if (!indices.Any())
return map.Select(MyHost.GetItemFromID);
return indices.Select(i => MyHost.GetItemFromID(map[i]));
}
// so you call
myList(); // for all items; decide on this API, may be separate to two methods?
myList(0); // or
myList(4); // or
myList(1, 3); // all loaded only on demand
Use a dictionary if you want more control over indexing. Ideally, for some reason this looks like you should be passing all IDs to DB in one go and SQL directly handle it, if that is the case.

LINQ to return null if an array is empty

public class Stuff
{
public int x;
// ... other stuff
}
I have a IEnumerable<Stuff> and I want to build a int[] of all of the x properties of all the Stuff objects in the collection.
I do:
IEnumerable<Stuff> coll;
// ...
var data = coll.Select(s => s.x).ToArray();
What I want is a null array rather than a int[0] if the collection is empty. In other words, if !coll.Any(), then I want data = null. (My actual need is that coll is an intermediate result of a complex LINQ expression, and I would like to do this with a LINQ operation on the expression chain, rather than saving the intermediate result)
I know that int[0] is more desirable than null in many contexts, but I am storing many of these results and would prefer to pass around nulls than empty arrays.
So my current solution is something like:
var tmp = coll.Select(s => s.x).ToArray();
int[] data = tmp.Any() ? tmp : null;
Any way to do this without storing tmp?
EDIT: The main question is how to do this without storing intermediate results. Something like NULLIF() from T-SQL where you get back what you passed in if the condition is false, and NULL if the condition is true.
If you're doing this a lot, you could write an extension method:
public static class IEnumerableExt
{
public static T[] ToArrayOrNull<T>(this IEnumerable<T> seq)
{
var result = seq.ToArray();
if (result.Length == 0)
return null;
return result;
}
}
Then your calling code would be:
var data = coll.Select(s => s.x).ToArrayOrNull();
Create the array only if coll is not empty, so the other way round:
int[] data = null;
if(coll.Any()) data = coll.Select(s => s.x).ToArray();
There's not a way to get Select to return null, but if you don't want to create an additional array you could do:
var tmp = coll.Select(s => s.x);
int[] data = tmp.Any() ? tmp.ToArray() : null;

C# Extension method instead of iteration

I was wondering, if there is an extension method that allow me to iterate a List and let me do the same thing with every item in the list. For example:
.RemoveAll(x => x.property == somevalue)
This removes every element wichs fulfill the condition. But, what if I have this:
foreach(object item in lstObjects)
{
object MyObject = new object();
MyObject.propertyone = item.property
MyObject.propertytwo = somevalue;
anotherlist.Add(MyObject);
}
Of course, the real code is a little more complex than this. My objective is to, instead of a foreach use an extension method, I have found List<T>.ForEach() but I can't get it to work, and this method does not exist in a var list. I found too .Select<>, .Where<> but this returns values, and in my method there is no need to return any value.
var convertedItems = lstObjects.Select(item =>
{
object MyObject = new object();
MyObject.propertyone = item.property
MyObject.propertytwo = somevalue;
return MyObject;
});
anotherList.AddRange(convertedItems);
or
anotherList = convertedItems.ToList();
and if you want to make it shorter:
var convertedItems = lstObjects.Select(item =>
new object {propertyone = item.property, propertytwo = somevalue});
I'm not sure I see why you want an extension method here. List<T>.ForEach() will do mostly what you like but your existing foreach loop is both idiomatic and readable. Is there a reason that you can't just write a normal function to do this?
public void DoMyThing(IList<object> objects) {
foreach (var obj in objects) {
someOtherList.Add(new MyObj() {
item1 = obj
});
}
}
In general if you find that you need to mutate items and not return values you don't want to use LINQ or query operators. Just use a foreach.
Edit: The answers suggesting Select() would work for this simple code, however you state
the real code is a little more complex than this
Which suggests to me that you may have to mutate some other state during iteration. The Select method will defer this mutation until the sequence is materialized; this will probably give you strange results unless you're familiar with how LINQ queries defer execution and capture outer variables.
It's trivial to write your own ForEach extension. I include the following in all of my code:
public static void ForEach<T>(this IEnumerable<T> collection, Action<T> action )
{
foreach (T item in collection)
{
action(item);
}
}
You can accomplish this via a Select statement:
var newList = lstObjects.Select(o =>
new { propertyone = o.property,
propertytwo = somevalue }).ToList();
Here is how you use ForEach with a lambda expression:
lstObjects.ForEach(item =>
{
MyObject obj = new MyObject();
obj.propertyone = item.property;
obj.propertytwo = somevalue;
anotherlist.Add(obj);
});
However as you can see it looks remarkably similar to what you already have!
Alternatively it looks to me like Select might be a better match for what you want to do:
anotherList.AddRange(lstObjects.Select(item => new MyObject()
{
propertyone = item.property,
obj.propertytwo = somevalue,
}));
List<MyObjectType> list = new List<MyObjectType>();
list.ForEach((MyObjectType item) => {
object MyObject = new object()
{
MyObject.propertyone = item.property,
MyObject.propertytwo = somevalue
};
anotherlist.Add(MyObject);
});
If you want to perform an action as part of an iteration, you might want to consider the .Do method which is part of the Interactive Extensions. See http://www.thinqlinq.com/Post.aspx/Title/Ix-Interactive-Extensions-return.
You can easily create an extension method to do this:
public IEnumerable<T> RemoveAll(this List<T> list, Func<bool, T> condition)
{
var itemsToRemove = list.Where(s => condition(s));
list.RemoveAll(itemsToRemove);
}
and you could then call it like this:
myList.RemoveAll(x => x.Property == someValue);
Edit: Here is another method for doing the same.
As far as 'built-in' goes there is no .ForEach(); however I think .Aggregate() would be the most appropriate option here (if you absolutely and utterly want a built-in function).
lstObjects.Aggregate(anotherList, (targetList, value) =>
{
object MyObject = new object();
MyObject.propertyone = item.property
MyObject.propertytwo = somevalue;
targetList.Add(MyObject);
return targetList;
});
You can obviously just write your own extension methods:
public static IEnumerable<T> Intercept<T>(this IEnumerable<T> values, Action<T> each)
{
foreach (var item in values)
{
each(item);
yield return item;
}
}
public static IEnumerable<T> Intercept<T>(this IEnumerable<T> values, Action<T, int> each)
{
var index = 0;
foreach (var item in values)
{
each(item, index++);
yield return item;
}
}
// ...
a.Intercept(x => { Console.WriteLine(x); }).Count();
NB: The reason I don't create a ForEach like everyone else, is because Microsoft didn't include it because, by design Linq methods always return a value, or list of values.
Specifically to your question, .Select<T> will do the trick.
anotherList.AddRange(lstObjects.Select(x => new MyObject()
{
propertyone = x.property,
propertytwo = somevalue
}));

how call method without return-type in linq?

i like to call a method without return-type in linq or in extension methods in linq?
Here my class i have situation line this
Class A
{
int i;
public int K
{
get { return i; }
set { i = value; }
}
public void calculate(int z)
{
this.k=z;
}
}
i like to do like this
List<A> sam = new List<A>();
//add elements to this list
var c = sam.Select( s => s.calculate(4) );
this sample only , i like to do like this for my purpose.
You should use List<T>.ForEach here.
sam.ForEach(s => s.calculate(somenumber));
I think you use .Select in your question because you want to get the results(all the instances of A after calling calculate). You can get them directly by the variable sam. ForEach modifies each elements of sam, and the "changes" are applied to the list itself.
If you mean that you want to iterate a sequence (IEnumerable) and invoke code for it, you can inplement an extension method with an action, that is invoked for each item in the sequence, e.g.:
public static void ForEach<T>(this System.Collection.Generic.IEnumerable<T> list, System.Action<T> action)
{
foreach (T item in list)
action(item);
}
This makes sense if you want to invoke small logic (one line) without implementing a foreach() block:
public class MyClass
{
public void DoSomethingInOneLine()
{
// do something
}
}
public static void Test(System.Collections.Generic.IEnumerable<MyClass> list)
{
list.ForEach(item => item.DoSomethingInOneLine());
}
If you don't need the result, you can fill the result with a random value (e.g. false).
var c = sam.Select( s => {s.calculate(4); return false;} );
I recently ran into this issue. I sometimes find I prefer the declerative syntax of LINQ...
this was my call
// wont compile:
from ticket in actualTickets
group ticket by ticket.ID into ticketGroup
select AddToBasket( exhibition, ticketGroup.First(), ticketGroup.Count() );
I couldn't think of a good reason to make AddToBasket() return anything, so I refactored as follows:
var pendingOpperations = from ticket in actualTickets
group ticket by ticket.ID into ticketGroup
select new Action( () => AddToBasket( exhibition, ticketGroup.First(), ticketGroup.Count() ) );
foreach ( var action in pendingOpperations ) action.Invoke();
Using this often:
Generic approach:
from item in sequence
// wrapping statements with lambda
let #void = new Func<bool>(() => {
// whatever you like..
return true;
})()
select item
If you want to do property assignment (bonus: example how to work with HTTP client :-):
..
// inside fluent LINQ query
let client = new HttpClient()
// initialise property and discard result
let #discard = client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes("user:pass")))
// now work with initialised client according to your logic..
select client.GetAsync("url").Result.Content.ReadAsStringAsync().Result
I had the same requirement recently, call the action reactively and I write a Do() stream processing function for 1) wrapping the action into a functor with a return value and 2) selecting on the stream.
public static IEnumerable<TSource> Do<TSource>(this IEnumerable<TSource> source,
Action<TSource> action) {
TSource doSelector(TSource src) {
action.Invoke(src);
return src;
}
return source
.Select(it => doSelector(it));
}
Please note that this utility function still has to convert the stream into List() to literally call the action for each stream item.
var numbers = new List<int> { 1, 2, 3 };
var sum = 0;
numbers
.Do(it => { sum += it; })
.ToList();

Categories