Loopa lista att ta var fjärde objekt C#

Permalänk
Medlem

Loopa lista att ta var fjärde objekt C#

Tja, har ett program som ska beräkna grupper. Gruppen bestäms av en lista deltagare i en tävling genom en databas. Databasen hämtar, skapar listan och passar den vidare till klassen "lista". "Lista" beräknar sen hur många "grupper" som behövs för att varje deltagare ska få vara med. En grupp får inte bestå av bara en deltagare... Är inte helt säker på hur jag löser det här. Så här långt har jag kommit, är inte heller helt säker på om det funkar:

Om en grupp blir 3 och en annan 1 ska en från grupp #1 till grupp #2 så det blir två grupper med 2....

private void CalculateGroups() { //Hämtar alla medlemmar och placerar den i nästa lista foreach (Members mems in q.GetAllIdFromCompetitionMembers(this.competition)) { mem.Add(mems); } //loopar listan för att skapa ett gruppobjekt i listan groupList. for (int i = 0; i < mem.Count(); i += 4) { groupList.Add(new Group(mem[i - 3], mem[i - 2], mem[i - 1], mem[i])); } }

Jag är medveten om att koden kommer krascha om det inte finns nog indexer för att skapa en grupp, vet inte hur jag löser det dock.

Konstruktor för Group

private Members[] memberList = new Members[3]; public Group(Members mem, Members mem1, Members mem2, Members mem3) { //kod här }

Konstruktor för Members

public Members( string id, int accIdType, int handicap, int status, string address, string city, string email, string firstname, string lastname, string password, long phone, int code) { //kod här }

Resultatet borde (helst) bli såhär:

[groupList[i].Header]
[groupList[i].Members.Firstname]
[groupList[i].Members.Firstname]
[groupList[i].Members.Firstname]
[groupList[i].Members.Firstname]
[blankt]
[groupList[i].Header]
[groupList[i].Members.Firstname]
[groupList[i].Members.Firstname]
[groupList[i].Members.Firstname]
[groupList[i].Members.Firstname]

etc när jag senare printar listan till en kontroll. Det är inga problem att göra dock.

Visa signatur

Citera om du vill ha svar, hjälpte jag dig, gilla svaret!
Felkod40

Permalänk
Hedersmedlem

Det där borde väl nästan fungera, förutsatt att du ändrar till

for (int i = 3; i < mem.Count(); i += 4)

?

Permalänk
Medlem
Skrivet av Elgot:

Det där borde väl nästan fungera, förutsatt att du ändrar till

for (int i = 3; i < mem.Count(); i += 4)

?

Sant. Men vad händer vid förekomsten av ett ojämt ?

Exempelvis blir ju ekvationen:

x / 4 * 10 / 60 = 5,5 om x = 133, dock är x / 4 = 33,25 som kräver en upprundning (ToZero), så 33 men 33 * 4 = 132, en blir över på grund av avrundningen. Då måste jag hierarkiskt loopa listorna och lägga till en person från en annan grupp till gruppen med bara 1, så att gruppen får 2 istället.

x / 4 skapar gruppen
x / 4 * 10 ger gruppen tid
x / 4 * 10 / 60 beräknar timmarna det tar

Visa signatur

Citera om du vill ha svar, hjälpte jag dig, gilla svaret!
Felkod40

Permalänk
Hedersmedlem
Skrivet av freddyfresh:

Sant. Men vad händer vid förekomsten av ett ojämt ?

Då blir (föregående) i + 4 större än mem.Count(), vilket leder till att de sista 1, 2 eller 3 elementen hoppas över. Lämpligen bör man väl i så fall skapa en ny grupp med dessa (och eventuellt ta en medlem från en grupp som redan har fyra om det bara blir en över).

Permalänk

Räkna ut hur många grupper du ska ha först. Har du minst dubbelt så många grupper som deltagare så behöver ingen vara själv.
När du räknat ut hur många grupper du ska ha (fyra kanske?) så är det bara att loopa igenom alla deltagare och tilldela de en grupp (och sen öka på gruppen som nästa person ska tilldelas, börja om från början om det är en för hög grupp.)

Typ nåt sånt här:

int groupCount = 4; int group = 0; foreach(Member member in MembersList) { member.AddToGroup(group); //tilldela en grupp (en funktion att göra på Member-klassen) if (++group == groupCount) { group = 0; } // loopa group-räknaren (dvs. 4 grupper blir 0 till 3) }

Visa signatur

Nikon D90, Nikon D7000, SB-600, 2xYN-460, Nikkor 18-105mm/3.5-5.6 VR, Nikkor 35mm/1.8, Nikkor 50mm/1.8D, Tamron 70-200/2.8, Tokina 11-16/2.8, Nikon 85/3.5 Macro
Flickr

Permalänk
Medlem
Skrivet av '[ABF:

Henkeman;12375799']

int groupCount = 4; int group = 0; foreach(Member member in MembersList) { member.AddToGroup(group); //tilldela en grupp (en funktion att göra på Member-klassen) if (++group == groupCount) { group = 0; } // loopa group-räknaren (dvs. 4 grupper blir 0 till 3) }

Men hur?
Jag är medveten om hur loopen utförs, jag har möjlighet att beräkna antal grupper behövda genom min ekvation ovanför (x / 4), så lås oss säga att X = 132. Då får vi y = 33 där y beskriver antal behövda grupper.
Men vad händer som sagt med 133 stycken? Jag måste ju fortfarande loopa för att flytta en medlem. Detta på grund av att en grupp får inte vara mindre än 2, 2 är okej men lämpligare vore ju som följande:

Grupp 1: 4
Grupp 2: 4
Grupp 3: 4
Grupp 4: 2

Grupp 3 skänker Grupp 4 en. Resultat:

Grupp 1: 4
Grupp 2: 4
Grupp 3: 3
Grupp 4: 3

Sen måste ju konstruktorn för min klass kunna ta emot en varierande mängd av medlemmar då den array max får bestå av 4, så den måste ursprungligen ta emot en array, placera alla objekt i array som en member i dess egna member-array och sen fortsätta med nästa grupp.

Det innebär att jag skulle kunna skicka en siffra mellan 0-3 (antagande att 0 är en indexeringsplats (egentligen: 1-4)) i antal medlemmar till konstruktorn för att designa ett objekt av det. Nu är ju dock igen problemet, hur gör jag loopen som ser till att medlemmarna i Grupp 3 skänker bort för att stabilisera möjligheten för en Grupp (ex 4) att vinna mot en Grupp 1 eller Grupp 2? Tänk att jag kommer ha ca 33-X antal grupper ungefär.

Antaget om X = 10

X / 4 = 2,5. 2,5 kan inte skapa en grupp då det blir

1: 4
2: 4
3: 2

Antag: 1 skänker, 2 skänker.

1: 3, 2: 3, 3: 3. I slutändan måste vi skapa en fjärde grupp, för vi saknar en person (3*3 = 9).
Nya: 1: 2, 2: 2, 3: 2, 4: 2. Grupperna är jämna och kan användas eftersom ingen understiger minimum.

En "tävling" ställs in om inte sökande är nog för att skapa minimalt 2 grupper så det användarfallet behöver man inte oroa sig för.'

Jag är säker på att jag överdriver eller tänker för mycket kring slutresultatet snarare än att ta en enkel väg till resultatet. Kom gärna med förslag.

Visa signatur

Citera om du vill ha svar, hjälpte jag dig, gilla svaret!
Felkod40

Permalänk
Hedersmedlem
Skrivet av freddyfresh:

Men hur?
Jag är medveten om hur loopen utförs, jag har möjlighet att beräkna antal grupper behövda genom min ekvation ovanför (x / 4), så lås oss säga att X = 132. Då får vi y = 33 där y beskriver antal behövda grupper.
Men vad händer som sagt med 133 stycken? Jag måste ju fortfarande loopa för att flytta en medlem. Detta på grund av att en grupp får inte vara mindre än 2, 2 är okej men lämpligare vore ju som följande:

Grupp 1: 4
Grupp 2: 4
Grupp 3: 4
Grupp 4: 2

Grupp 3 skänker Grupp 4 en. Resultat:

Grupp 1: 4
Grupp 2: 4
Grupp 3: 3
Grupp 4: 3

Behåll loopen som den är och fixa efteråt. När den har körts vet du att groupList innehåller grupper med fyra medlemmar och att det finns mem.Count() % 4 personer kvar att placera. Sedan skulle man väl kunna försöka sig på något liknande

if(mem.Count() % 4 == 3) //skapa en grupp med tre medlemmar groupList.Add(new Group(mem[mem.Count()-3], mem[mem.Count() - 2], mem[mem.Count() - 1])); //fixa konstruktorn else if(mem.Count() % 4 == 2) { if(groupList.Count() > 0) //stjäl en medlem groupList.Add(new Group(mem[mem.Count()-2], mem[mem.Count() - 1], groupList.Last().removeMember())); //fixa removeMember else //bara två medlemmar groupList.Add(new Group(mem[mem.Count() - 2], mem[mem.Count() - 1])); //fixa konstruktorn } else if(mem.Count() % 4 == 1) { if(groupList.Count() > 1) //stjäl två medlemmar groupList.Add(new Group(mem[mem.Count() - 1], groupList[groupList.Count()-2].removeMember(), groupList.Last().removeMember())); else if(groupList.Count() > 0) //stjäl en medlem groupList.Add(new Group(mem[mem.Count() - 1], groupList.Last().removeMember())); //fixa konstruktorn else //bara en deltagare ;//hantera situationen }

Permalänk
Medlem
Skrivet av freddyfresh:

Men hur?
Jag är medveten om hur loopen utförs, jag har möjlighet att beräkna antal grupper behövda genom min ekvation ovanför (x / 4), så lås oss säga att X = 132. Då får vi y = 33 där y beskriver antal behövda grupper.
Men vad händer som sagt med 133 stycken? Jag måste ju fortfarande loopa för att flytta en medlem. Detta på grund av att en grupp får inte vara mindre än 2, 2 är okej men lämpligare vore ju som följande:

Grupp 1: 4
Grupp 2: 4
Grupp 3: 4
Grupp 4: 2

Grupp 3 skänker Grupp 4 en. Resultat:

Grupp 1: 4
Grupp 2: 4
Grupp 3: 3
Grupp 4: 3

Sen måste ju konstruktorn för min klass kunna ta emot en varierande mängd av medlemmar då den array max får bestå av 4, så den måste ursprungligen ta emot en array, placera alla objekt i array som en member i dess egna member-array och sen fortsätta med nästa grupp.

Det innebär att jag skulle kunna skicka en siffra mellan 0-3 (antagande att 0 är en indexeringsplats (egentligen: 1-4)) i antal medlemmar till konstruktorn för att designa ett objekt av det. Nu är ju dock igen problemet, hur gör jag loopen som ser till att medlemmarna i Grupp 3 skänker bort för att stabilisera möjligheten för en Grupp (ex 4) att vinna mot en Grupp 1 eller Grupp 2? Tänk att jag kommer ha ca 33-X antal grupper ungefär.

Antaget om X = 10

X / 4 = 2,5. 2,5 kan inte skapa en grupp då det blir

1: 4
2: 4
3: 2

Antag: 1 skänker, 2 skänker.

1: 3, 2: 3, 3: 3. I slutändan måste vi skapa en fjärde grupp, för vi saknar en person (3*3 = 9).
Nya: 1: 2, 2: 2, 3: 2, 4: 2. Grupperna är jämna och kan användas eftersom ingen understiger minimum.

En "tävling" ställs in om inte sökande är nog för att skapa minimalt 2 grupper så det användarfallet behöver man inte oroa sig för.'

Jag är säker på att jag överdriver eller tänker för mycket kring slutresultatet snarare än att ta en enkel väg till resultatet. Kom gärna med förslag.

Detta borde fungera:

public static class IEnumerableExtensions { public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> ie, int size) { var arr = ie.ToArray(); var result = new List<List<T>>(); for (var i = 0; i < arr.Length; i++) { var n = i % size; if (n == 0) result.Add(new List<T>()); result[i / size].Add(arr[i]); } return result; } }

edit: fungerar naturligtvis inte pga gruppens minimkrav.. ska klura lite mera

edit2: uppdaterad version!

public static class IEnumerableExtensions { public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> ie, int groups, int minSize) { var list = ie.ToList(); if (list.Count / groups < minSize) throw new ArgumentException("Input too small for size"); var result = doPartition(list.Take(groups * minSize).ToList(), groups); var rest = doPartition(list.Skip(groups * minSize).ToList(), groups); for (var i = 0; i < rest.Count; i++) { result[i].AddRange(rest[i]); } return result; } private static List<List<T>> doPartition<T>(List<T> list, int groups) { var size = (list.Count + groups-1) / groups; var result = new List<List<T>>(); for (var i = 0; i < list.Count; i++) { if (i % size == 0) result.Add(new List<T>()); result[i / size].Add(list[i]); } return result; } }

fult :\

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Medlem

Jag har förmodligen missat något i själva problemet, men borde inte detta funka?
Inget fancy utan bara några loopar.

class Program { static void Main(string[] args) { int memberCount = 10; int maxMembersPerGroup = 4; int groupCount = (int)Math.Ceiling((decimal)memberCount / maxMembersPerGroup); var members = new List<Member>(); for (int i = 1; i <= memberCount; ++i) { members.Add(new Member { Id = i }); } var groups = new List<Group>(); for (int i = 1; i <= groupCount; ++i) { groups.Add(new Group { Id = i }); } int counter = 0; foreach (var member in members) { groups[counter++ % groupCount].Members.Add(member); } foreach (var group in groups) { Console.WriteLine(group); } } } public class Member { public int Id { get; set; } public override string ToString() { return string.Format("Member {0}", Id); } } public class Group { public int Id { get; set; } public ICollection<Member> Members { get; set; } public Group() { Members = new List<Member>(); } public override string ToString() { var sb = new StringBuilder(); sb.AppendFormat("Group {0}\r\n", Id); foreach (var member in Members) sb.AppendFormat("\t{0}\r\n", member); sb.AppendLine(); return sb.ToString(); } }

Visa signatur

as far as we can tell, the massacre went well...

Permalänk
Medlem
Skrivet av jovnas:

Jag har förmodligen missat något i själva problemet, men borde inte detta funka?
Inget fancy utan bara några loopar.

class Program { static void Main(string[] args) { int memberCount = 10; int maxMembersPerGroup = 4; int groupCount = (int)Math.Ceiling((decimal)memberCount / maxMembersPerGroup); var members = new List<Member>(); for (int i = 1; i <= memberCount; ++i) { members.Add(new Member { Id = i }); } var groups = new List<Group>(); for (int i = 1; i <= groupCount; ++i) { groups.Add(new Group { Id = i }); } int counter = 0; foreach (var member in members) { groups[counter++ % groupCount].Members.Add(member); } foreach (var group in groups) { Console.WriteLine(group); } } } public class Member { public int Id { get; set; } public override string ToString() { return string.Format("Member {0}", Id); } } public class Group { public int Id { get; set; } public ICollection<Member> Members { get; set; } public Group() { Members = new List<Member>(); } public override string ToString() { var sb = new StringBuilder(); sb.AppendFormat("Group {0}\r\n", Id); foreach (var member in Members) sb.AppendFormat("\t{0}\r\n", member); sb.AppendLine(); return sb.ToString(); } }

Det ser ut att funka men får det inte att funka. Vad är new Group { Id = 1}? Borde det inte vara en konstruktor som säger att Id blir 1 genom this.id = id?

Och förklara gärna hur den här funkar, egenskapen eller metoden Members verkar inte existera genom
groups[counter++ % groupCount].Members.

Så vad gör de?

Edit: Efter mindre justeringar i tillgänglighet och ändringar så funkar koden. Ska testa och se om den funkar som förutsatt nu

Visa signatur

Citera om du vill ha svar, hjälpte jag dig, gilla svaret!
Felkod40

Permalänk
Medlem

Strunta i att ha en Group som tar fyra medlemmar, lägg till varje medlem en och en istället, och lägg in en fråga i varje Group om den är full.

class Group { int maxMembers = 4; void addMember(Member m) { // add m to list. } bool isFull() { return members.length >= maxMembers; } String toString() { String retval = "[groupList[i].Header]"; // eller vad nu det var foreach ( Member m in memberList ) retval += m.name; // eller vad det är du vill ha ut från Member return retval; }

koden för att bygga grupper blir då:

Vector<Group> groups = new Vector<Groups>(); groups.add(new Group()); foreach ( Member m in blahblah ) { int index = groups.length-1; if ( groups[index].isFull() ) { groups.add(new Group()); index++; } groups[index].addMember(m); } foreach ( Group g in groups) Console.WriteLn(g.toString());

Det är en mer objektorienterad lösning. Syntaxen är åt fanders, jag kodade allt i browsern, men principen borde synas.

Edit: public override string toString i inläggen ovan är givetvis bättre än skräpet jag la till här. Ville visa principen, inte make it pretty

Permalänk
Medlem
Skrivet av freddyfresh:

Det ser ut att funka men får det inte att funka. Vad är new Group { Id = 1}? Borde det inte vara en konstruktor som säger att Id blir 1 genom this.id = id?

// detta: Group group = new Group { Id = 1 }; // är samma sak som detta: Group group = new Group(); group.Id = 1;

Se How to: Initialize Objects by Using an Object Initializer (C# Programming Guide)

Skrivet av freddyfresh:

Och förklara gärna hur den här funkar, egenskapen eller metoden Members verkar inte existera genom groups[counter++ % groupCount].Members.

int counter = 0; foreach (var member in members) { groups[counter++ % groupCount].Members.Add(member); } // Man kan skriva koden ovan ungefär såhär: int counter = 0; foreach (var member in members) { int index = counter % groupCount; // ger en siffra mellan 0 och groupCount med hjälp av modulo-operatorn. groups[index].Members.Add(member); // Lägger till member i Members-listan i den Group som pekas ut av indexet. counter = counter + 1; // öka på räknaren. }

Se % Operator (C# Reference)

Visa signatur

as far as we can tell, the massacre went well...

Permalänk
Medlem
Skrivet av IonSwitz:

Strunta i att ha en Group som tar fyra medlemmar, lägg till varje medlem en och en istället, och lägg in en fråga i varje Group om den är full.

class Group { int maxMembers = 4; void addMember(Member m) { // add m to list. } bool isFull() { return members.length >= maxMembers; } String toString() { String retval = "[groupList[i].Header]"; // eller vad nu det var foreach ( Member m in memberList ) retval += m.name; // eller vad det är du vill ha ut från Member return retval; }

koden för att bygga grupper blir då:

Vector<Group> groups = new Vector<Groups>(); groups.add(new Group()); foreach ( Member m in blahblah ) { int index = groups.length-1; if ( groups[index].isFull() ) { groups.add(new Group()); index++; } groups[index].addMember(m); } foreach ( Group g in groups) Console.WriteLn(g.toString());

Det är en mer objektorienterad lösning. Syntaxen är åt fanders, jag kodade allt i browsern, men principen borde synas.

Edit: public override string toString i inläggen ovan är givetvis bättre än skräpet jag la till här. Ville visa principen, inte make it pretty

Skulle väl kräva att vi aldrig stänger av programmet och att man alltid sköter allt på en och samma plats?
Ska kunna utföras vid behov och ska helst kunna jobba med ca 290 medlemmar samtidigt.

Skrivet av jovnas:

// detta: Group group = new Group { Id = 1 }; // är samma sak som detta: Group group = new Group(); group.Id = 1;

Se How to: Initialize Objects by Using an Object Initializer (C# Programming Guide)

int counter = 0; foreach (var member in members) { groups[counter++ % groupCount].Members.Add(member); } // Man kan skriva koden ovan ungefär såhär: int counter = 0; foreach (var member in members) { int index = counter % groupCount; // ger en siffra mellan 0 och groupCount med hjälp av modulo-operatorn. groups[index].Members.Add(member); // Lägger till member i Members-listan i den Group som pekas ut av indexet. counter = counter + 1; // öka på räknaren. }

Se % Operator (C# Reference)

Tack. Har fixat och tror den funkar nu.

Visa signatur

Citera om du vill ha svar, hjälpte jag dig, gilla svaret!
Felkod40

Permalänk
Medlem
Skrivet av freddyfresh:

Skulle väl kräva att vi aldrig stänger av programmet och att man alltid sköter allt på en och samma plats?
Ska kunna utföras vid behov och ska helst kunna jobba med ca 290 medlemmar samtidigt.

Tack. Har fixat och tror den funkar nu.

Fast där fylls inte alla grupper förutom en vad jag kan se. Med min lösning får du grupperna 4 4 4 2 med 14 medlemmar och 4 grupper, minGroup på 2

edit: nu har jag läst en extra gång och uppenbarligen har jag krånglat till det för mig. Här är en mer funktionell lösning för skojs skull (och som ger det resultat du är ute efter ):

var numberOfGroups = 4; var indices = Enumerable.Range(0, memberIds.Count); var groups = memberIds.Zip(indices, (member, index) => Tuple.Create(member, index)) .GroupBy(t => t.Item2 % numberOfGroups) .Select(group => Tuple.Create(group.Key + 1, group.Select(t => t.Item1))); // för att skriva innehållet: foreach(var group in groups) { Console.WriteLine(string.Format("Id: {0} | Members: {1}", group.Item1, string.Join(", ", group.Item2))); }

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Medlem
Skrivet av freddyfresh:

Skulle väl kräva att vi aldrig stänger av programmet och att man alltid sköter allt på en och samma plats?
Ska kunna utföras vid behov och ska helst kunna jobba med ca 290 medlemmar samtidigt.

Antalet medlemmar spelar givetvis ingen roll i det här exemplet, men klart att man kan skapa olika index för att optimera prestanda. Det är dock inget problem i det här exemplet. 290 medlemmar är ju bra nära noll så länge operationerna inte är ordo n2 eller mer.

Jag missade kravet att ingen grupp får innehålla bara en medlem, men det är å andra sidan en trivial fix. Invändningen att man alltid sköter allt på en och samma plats förstår jag inte alls. Ersätt "blahblah" med den där "GetAllIdFromCompetitionMembers"-metoden så får du ju samma funktion som i inledningsinlägget?

Grundprincipen jag försökte visa på är att det är bättre att låta Group-klassen hantera antalet medlemmar och när den är full, osv, istället för att ha en mängd loopar i grundmetoden och bara använda Group-klassen som en datakontainer. Det senare är väldigt 'C' och kommer ligga dig i fatet om du söker jobb som programmerare inom objektorienterade språk.