2

I want to loop over a generic list of MemberProfiles:

List<MemberProfile> jobseekers    

, and group the items in a <div class='group'>tag. Every <div class="group"> should contain 3 jobseekers like this:

<div class="group">
    <div class="jobseeker">
        ...data jobseeker
    </div>
</div>

I tried different methods on generic list, like .skip() and .take() but I'm not succeeding in implementing this scenario.

My code looks like this:

foreach (MemberProfile jobseekerProfile in Jobseekers)
{
    if (jobseekerProfile != null)
    {
        Label lblJobseeker = new Label();
        StringBuilder sbJobseeker = new StringBuilder();
        sbJobseeker.Append(string.Format("<p><strong>{0}&nbsp;{1}</strong><br />", jobseekerProfile.FirstName, jobseekerProfile.LastName));
        XPathNodeIterator preValues = library.GetPreValues(1362);
        preValues.MoveNext();
        XPathNodeIterator iterator2 = preValues.Current.SelectChildren("preValue", "");
        while (iterator2.MoveNext())
        {
            if (jobseekerProfile.JobType == iterator2.Current.GetAttribute("id", ""))
            {
                sbJobseeker.Append(string.Format("looking for a {0}<br />", iterator2.Current.Value));
            }
        }
        XPathNodeIterator iterator3 = library.GetPreValues(1363);
        iterator3.MoveNext();
        XPathNodeIterator iterator4 = iterator3.Current.SelectChildren("preValue", "");
        StringBuilder sbJobExperience = new StringBuilder();
        string[] strJobExperience = jobseekerProfile.JobExperience.Split(new char[] { ',' });
        int counter = 1;
        while (iterator4.MoveNext())
        {
            if (jobseekerProfile.JobExperience.Contains(iterator4.Current.GetAttribute("id", "")))
            {
                if (counter != strJobExperience.Count<string>())
                {
                    sbJobExperience.Append(string.Format("{0}, ", iterator4.Current.Value));
                    counter++;
                }
                else
                {
                    sbJobExperience.Append(string.Format("{0}", iterator4.Current.Value));
                }
            }
        }
        sbJobseeker.Append(string.Format("Fields of experience: {0}<br />", sbJobExperience.ToString()));
        sbJobseeker.Append(string.Format("Years of experience: {0}<br />", jobseekerProfile.YearsExperience));
        sbJobseeker.Append(string.Format("Country: {0}<br />", jobseekerProfile.Country));
        sbJobseeker.Append(string.Format("<form name='frmSelect' action='/selectjobcandidate.aspx' method='post'><input type='hidden' name='username' value='{0}' /><input type='submit' value='select candidate' /></form>", jobseekerProfile.UserName));
        lblJobseeker.Text = sbJobseeker.ToString();
        phListJobseekers.Controls.Add(lblJobseeker);
    }
}    

can someone put me on the right track to implement this scenario?

Rob
  • 4,927
  • 12
  • 49
  • 54
Toontje
  • 1,415
  • 4
  • 25
  • 43

4 Answers4

16
for (var i = 0; i < Jobseekers.Count; i += 3)
{
    foreach (MemberProfile jobseekerProfile in Jobseekers.Skip(i).Take(3))
    {

    }
}

Is this what you are after?

Rob
  • 4,927
  • 12
  • 49
  • 54
MortenRøgenes
  • 676
  • 4
  • 12
  • Hi MortenRogenes, yes, this seems like a very elegant looping construction. I'll give it a try and let you now if it worked. – Toontje Jul 24 '12 at 07:39
  • I think this is the best solution as it's very clear what it does when the code is read. – Anders Arpi Jul 24 '12 at 07:44
  • Depending on whether the compiler is able to avoid doing the "Skip" thing on every loop iteration, this might also be the slowest of all proposed solutions (if Skip has to step through the whole IEnumerable again and again). – Timbo Jul 24 '12 at 07:54
  • @Timbo The compiler won't optimize this to apply the Skip once. Skip and Take are just extension methods, with (I believe, although I'm not a C# compiler dev) no special-casing from the compiler. So this *is* a quadratic algorithm. – Adam Mihalcin Jul 24 '12 at 08:02
  • Hi MortenRogenes, I implemented your loop, but the result is not what I hoped for. Now
    elements are nested whitin
    elements. My List contains 6 jobseekers. But there are 21 duplicate jobseekers rendered on the page.
    – Toontje Jul 24 '12 at 08:04
  • Are you adding 3 to the counter? And are you opening/closing "group" tag around the foreach statement? – MortenRøgenes Jul 24 '12 at 08:16
  • Hi Mortensen, your loops works fine. I did some rewritings on my on code and everythings works fine now. Thanks a lot for your help. – Toontje Jul 24 '12 at 11:32
  • Glad you figured it out. As has been already pointed out, you should consider optimizing the algorithm if you have a lot of jobseekers. =) – MortenRøgenes Jul 24 '12 at 12:18
  • @MortenRøgenes thank you for that elegant solution, it was what I was looking for in 2 days :D – Ange1 Oct 29 '15 at 10:14
1

Since you seem to want to do it with linq, here we go:

var tagged = Jobseekers.Select((x, i) => new { x, i });
var grouped = tagged.ToLookup(t => (t.i - 1) / 3, t => t.x);

foreach (var group in grouped)
{
    Console.WriteLine("Group:");
    foreach (var item in group)
    {
        Console.WriteLine(item);
    }
}
Timbo
  • 27,472
  • 11
  • 50
  • 75
  • hi Timbo, Thanks for your code. I tried to implement it but I get several build errors: Error 1 Operator '-' cannot be applied to operands of type 'UmbracoRegistration.MemberProfile' and 'int' C:\repos\umbracoregistration\UmbracoRegistration\UmbracoRegistration\Usercontrols\ListJobseekers.ascx.cs 32 63 UmbracoRegistration Error 2 Cannot convert lambda expression to type 'System.Collections.Generic.IEqualityComparer' because it is not a delegate type – Toontje Jul 24 '12 at 08:21
  • @Toontje My fault, the x and i had to be swapped in the first line. This is what happens when testing with a list of integers :( – Timbo Jul 24 '12 at 11:02
  • Hi Timbo, I swapped the x an i, but I still get compiler errors: Error 2 Operator '-' cannot be applied to operands of type 'umbraco.cms.businesslogic.web.Document' and 'int' my list items are of type Document (Umbraco object) – Toontje Jul 25 '12 at 08:35
0

From the following answer : Split List into Sublists with LINQ

public static List<List<MemberProfile>> Split(List<MemberProfile> source)
{
    return  source
        .Select((x, i) => new { Index = i, Value = x })
        .GroupBy(x => x.Index / 3)
        .Select(x => x.Select(v => v.Value).ToList())
        .ToList();
}

See other answers as well

Community
  • 1
  • 1
mathieu
  • 30,974
  • 4
  • 64
  • 90
  • ok, I'll try out the sublist LINQ example. I guess the 'source' is my generic List jobseekers list? – Toontje Jul 24 '12 at 07:35
0

Your question isn't completely clear, but it appears that you want to split your jobseekers list into groups of 3 elements each, and then perform some manipulation on each group of 3 to get some HTML snippet, so the output is a sequence of HTML snippets.

Let's assume that jobseekers always has a multiple of 3 elements. If it doesn't always have 3 elements, this solution will leave the last group with the remainder of the elements, and you can filter out that group by dropping any group with fewer than 3 elements (that Where would go just after the GroupBy). With LINQ:

IEnumerable<string> htmlSnippets = jobseekers
    .Zip(Enumerable.Range(0, jobseekers.Count), Tuple.Create)
    .GroupBy(tup => tup.Item2 / 3)
    .Select(GenerateHtmlString);
string combinedHtmlString = string.Join(string.Empty, htmlSnippets);

public string GenerateHtmlString(IEnumerable<MemberProfile> profiles)
{
    return string.Format(@"<div class=""jobseeker"">{0}</div>",
        // The question doesn't specify how the 3 jobseekers are rendered in HTML
        );
}
Adam Mihalcin
  • 14,242
  • 4
  • 36
  • 52