3
votes

I have two IEnumerable objects, if a specific condition is met, i need to take each element from first IEnumerable to second IEnumerable. My code is like this

IEnumerable<IPublishedContent> nodesTemp = posts.Take(10).ToList();
IEnumerable<IPublishedContent> nodes = null;
foreach (var n in nodesTemp)
{
    if (condition=true)
    {
        nodes.add(n);           
    }
}

But this throws an error

: 'System.Collections.Generic.IEnumerable<Umbraco.Core.Models.IPublishedContent>' does not contain a definition for 'add' and no extension method 'add' accepting a first argument of type 'System.Collections.Generic.IEnumerable<Umbraco.Core.Models.IPublishedContent>' could be found (are you missing a using directive or an assembly reference?)

Complete code for reference

 IEnumerable<IPublishedContent> nodesTemp = posts.Take(count).ToList();

IEnumerable<IPublishedContent> nodes = null;
foreach (IPublishedContent n in nodesTemp)
{
    var rolename = n.GetProperty("focusedUserGroup").Value.ToString();
    var username = umbraco.cms.businesslogic.member.Member.GetCurrentMember().Text;
    var flag = false;

    if (!string.IsNullOrEmpty(rolename))
    {
        var groups = rolename.Split(',');

        foreach (var group in groups)
        {
            if (Roles.IsUserInRole(username, group))
            {
                nodes.add(n);
                break;
            }
        }

    }
}
3
You can not add in IEnumerable it's read only. take a List - Divyang Desai
@Div Can you suggest any work around so that i can take some specific item fron one IEnumerable to another? - None
It can be thousand of code lines. But you always can put it in fucntion. Then just use it in query. - eocron
No difference - you can refactor your predicate into as many functions as you like. Ultimately, all that 'condition' needs to be is a Func<IPublishedContent, bool> - StuartLC
Div's comment could be right. If you declare nodes as var nodes = new List<IPublishedContent>(); instead of = null before the loop, you can .Add (capital A) to that List<>. Such a List<> is implements IEnumerable<> (and the latter interface is even covariant). - Jeppe Stig Nielsen

3 Answers

4
votes

You can't do this. The reason is because IEnumerable just represents iterator over some collection. It can be array in memory, select from remote database, or even constant call like this:

IEnumerable<int> GetSomeConsts()
{
    yield return 1;
    yield return 101;
    yield return 22;
}

What you can do is to expand post iterator of your first collection. For example like this:

bool IsCondition(IPublishedContent n)
{
    var rolename = n.GetProperty("focusedUserGroup").Value.ToString();
    var username = umbraco.cms.businesslogic.member.Member.GetCurrentMember().Text;

    if (!string.IsNullOrEmpty(rolename))
    {
        var groups = rolename.Split(',');

        foreach (var group in groups)
        {
            if (Roles.IsUserInRole(username, group))
            {
                 return true;
            }
        }
    }
    return false;
}

Then just call it like this:

var nodes = posts.Take(count).Where(IsCondition);
1
votes

I think this is a great example of what you can use yield return for

eg:

public IEnumerable<IPublishedContent> Whatever(IEnmerable<IPublishedContent> nodes, int count)
{
    foreach(var node in nodes.Take(count))
    {
        var rolename = node.GetProperty("focusedUserGroup").Value.ToString();
        var username = umbraco.cms.businesslogic.member.Member.GetCurrentMember().Text;
        if (!string.IsNullOrEmpty(rolename))
        {
            var groups = rolename.Split(',');

            foreach (var group in groups)
            {
                if (Roles.IsUserInRole(username, group))
                {
                     yield return node; //yield return allows you to create a new enumerable inline
                     break;
                }
            }
        }
    }
}

yield return will execute your code while you enumerate it, so if you were to call Whatever(...).First() it would only run through the code until it found the first yield return.

0
votes

It looks like you're filtering an IEnumerable.

In such a case, you don't need to turn it into a list first, and you can use Enumerable<T>.Where() to filter it.

Something like this should work:

var nodesTemp = posts.Take(10);
var nodes = nodesTemp.Where(item => predicate(item));

Where predicate(item) is some function that takes a parameter of type IPublishedContent and returns a bool.

You can write that as a lambda expression, for example:

var nodesTemp = posts.Take(10);
var nodes = nodesTemp.Where(item => item.SomeProperty == someTargetValue);

If you need nodes to be a list rather than an IEnumerable<IPublishedContent>, you can turn it into a list:

var nodes = nodesTemp.Where(item => item.SomeProperty == someTargetValue).ToList();

At this point you probably don't really need nodesTemp (unless you're using it for debugging purposes):

var nodes = 
    posts.Take(10)
    .Where(item => item.SomeProperty == someTargetValue)
    .ToList();