Skip and take method in List - c#

I have a class Players. And I want to create Hyperlink with Skip and Take methods. But gives me System.Linq.Enumerable error. My goal is make a pyramid user list. here is my codes
public class Players
{
public string Name{ get; set; }
public int Order{ get; set; }
public int ID { get; set; }
}
List<Players> playerlist= new List<Players>();
playerlist= (from DataRow dr in dt.Rows
select new Players()
{
Name= (dr["name"].ToString()),
Order= int.Parse(dr["order"].ToString()),
ID = int.Parse(dr["Id"].ToString())
}).ToList();
playerlist= playerlist.OrderBy(x => x.Order).ToList();
int skip = 0;
int take = 1;
int addedCount = 0;
do
{
HyperLink links= new HyperLink();
links.Text = "" + playerlist.Skip(skip ).Take(take).Select(x => x.Name);
links.NavigateUrl = "playerdetails.aspx?id=" + oyunculistesi.Skip(skip).Take(take).Select(x => x.ID);
Page.Controls.Add(links);
addedCount += take ;
skip+= take ;
take += +1;
}
while (addedCount < playerlist.Count);
It is working with StringBuilder but with HyperLink not.
sb.AppendLine(string.Join(" ", players.Skip(skip).Take(take).Select(x => $"{x.Order}) {x.Name}")));

Your Select is returning an IEnumerable of char and you need to build a string from them by using string.Join like what you did in the StringBuilder:
linkuret.Text = string.Join("" , playerlist.Skip(skip).Take(take).Select(x => x.Name));

I would rewrite your loop in this way
int skip = 0;
while (skip < playerlist.Count)
{
HyperLink links= new HyperLink();
Players p = playerlist.Skip(skip).FirstOrDefault();
links.Text = $"{p.Name}"
links.NavigateUrl = $"playerdetails.aspx?id={p.Id}"
Page.Controls.Add(links);
skip++;
}
First I have removed the Take part from your code and used FirstOrDefault to get always the first element after the skip. Finally the Players elements is loaded just one time and then I used the properties of the class with more readable code.

Related

Extract overlapping substrings from a list of strings using LINQ

I have the following list of strings {"a","b","c","d","e"}. How can I obtain sublists of length 3 using LINQ like this:
{"a","b","c"}
{"b","c","d"}
{"c","d","e"}
I am not looking for every combination
var list = students.OrderBy(student => student.LastName)
.Select(student => student);
List<Student> sortedStudents = list.ToList();
var triplets = from x in sortedStudents
from y in sortedStudents
from z in sortedStudents
select new { x, y, z};
StudentListBox.ItemsSource = triplets;
I am not looking for something like
{"a","b","c"}
{"a","b","d"}
{"a","b","e"}
.............
{"d","a","b"}
{"d","a","c"}
{"d","a","e"} and so on
Student class
class Student
{
public Student()
{
}
public String FirstName
{
get;
set;
}
public String LastName
{
get;
set;
}
public DateTime Birthday
{
get;
set;
}
public override string ToString()
{
return FirstName + " " + LastName;
}
}
You can us an overload of Select which gets the index of current element as an extra parameter to the selector and use it like this:
var triplets = sortedStudents.Take(list.Count - 2)
.Select((x, i) => new { S1 = x, S2 = list[i+1], S3 = list[i+2] });
Here is one approach with Linq - .Take(3)defines length of 3
string[] input = { "a", "b", "c", "d", "e" };
var result = Enumerable.Range(0, input.Length - 2).Select(x => input.Skip(x).Take(3));
Just loop the strings in your array:
public IEnumerable<IEnumerable<string>> GetTriples(string[] myArray)
{
for (int i = 0; i < myArray.Length - 2; i++)
{
yield return myArray.Skip(i).Take(3);
}
}
This codes loops every string in your array and gets the next two strings.
Assuming (because you haven't got a complete code sample) that you want to take triples of items from your collection in the order in which they appear, you can use a combination of Skip and Take to give you subsets representing your triples.
var triplets = new List<IEnumerable<Student>>();
for(int i = 0; i < (sortedStudents.Count - 2); i++)
{
triplets.Add(sortedStudents.Skip(i).Take(3));
}

How to delete duplicates from structure array with int and string components

In my project I have
struct Cities{public int Name;public int n}
int n represents the population of a city.
I also have cities[] c;, that array will be filled with names and number of citizens in city.
Example:
c[0].Name="New York";c[0].n=845698;
I need to write a method that will erase all cities from the array which have same names (if there is some) and to add their population to first one.
This should do it:
struct Cities
{
public string name;
public int n;
}
[Test]
public void SomeMethod()
{
Cities[] c = new Cities[7];
c[0].name = "new york";
c[0].n = 10;
c[1].name = "detroit";
c[1].n = 20;
c[2].name = "las vegas";
c[2].n = 30;
c[3].name = "new york";
c[3].n = 40;
c[4].name = "detroit";
c[4].n = 50;
c[5].name = "chicago";
c[5].n = 60;
c[6].name = "chicago";
c[6].n = 70;
c = c.GroupBy(ct => ct.name)
.Select(cl => new Cities
{
name = cl.First().name,
n = cl.Sum(ct => ct.n)
}).ToArray();
foreach (var city in c)
{
Console.WriteLine($"city={city.name}, pop={city.n}");
}
}
If the situation is just as simple as you describe, you might want to consider a
Dictionary<string, int>
You would then never add a duplicate city, rather if the city already existed (use trygetvalue(key, out value) or something) you would simply add the population.
Otherwise you are stuck with a simple search to find duplicates, and a routine which combines the duplicates and removes one of them.

List Re-Order is mixing up property values across list items

I have a strange situation happening or maybe it is my misunderstanding of lists, or it is one of those cases where you look at it too long and am missing something simple.
When I re-order a list in this example, it is mixing up column values across rows in the list. Here are some pics as an example as captured during debugging.
In the below pic, first note the list ordering done on 783. Next note the ItemId value of position [0] in the list. It is 1121.
Now after just advancing from line 793 to 798, note the values of inventoryNeed after it has been assigned the value from lstInventoryNeeds[i]. Note that its ItemId value is 1336! And further, the same list entry in position [0] also changed JUST the ItemId value from 1121 to 1336! What just happened?!?!
Now if I comment out the original list re-order code on line 783 and run it again, see this pic, captured just after the inventoryNeed assignment when i = 0 just as before, but there is no mix up in the ItemId value.
Any idea why this is happening?
Here is code for this section, but not sure how useful it is taken out of context:
private ActionConfirmation<int> AllocateContainersForNeedList(List<AllocationNeedViewModel> lstInventoryNeedsOriginal, int intUserId, bool failOnShortage)
{
//Sort by this so if first alloc attempt fails for the remnant,
//then add that qty to next Need in list (if exists) and attempt allocate for full sheets
var lstInventoryNeeds = lstInventoryNeedsOriginal;
//.OrderBy(x => x.ItemId)
//.ThenByDescending(x => x.IsRemnant)
//.ToList();
ActionConfirmation<int> allocateResult = ActionConfirmation<int>.CreateSuccessConfirmation("Allocation of Containers for Needs List Successful.",1);
bool firstOfTwoNeedsForSameItem = false;
AllocationNeedViewModel inventoryNeed;
for (int i = 0; i < lstInventoryNeeds.Count; i++)
{
inventoryNeed = lstInventoryNeeds[i];
//If this is an inventory tracked item, allocate it
if (boolIsInvenTrackedItem(inventoryNeed.ItemId))
{
//Allocate the required inventory for the need just created
if (lstInventoryNeeds.Count() > i + 1)
{
firstOfTwoNeedsForSameItem = inventoryNeed.IsRemnant && (lstInventoryNeeds[i + 1].ItemId == inventoryNeed.ItemId);
}
else
{
firstOfTwoNeedsForSameItem = false;
}
allocateResult =
AllocInvenForNeed(
inventoryNeed.FacilityId,
inventoryNeed,
intUserId,
0,
0,
!firstOfTwoNeedsForSameItem
);
//Create Allocation Error
if (!allocateResult.WasSuccessful)
{
//Check if original attempt was for a Remnant
if (firstOfTwoNeedsForSameItem)
{
//Add current remnant need to the next need for this item, which will then get allocated on the next loop
lstInventoryNeeds[i + 1].QtyNeeded = lstInventoryNeeds[i + 1].QtyNeeded + inventoryNeed.QtyNeeded;
}
else
{
return allocateResult;
}
}
} //if inventory tracked item
}//for loop
return allocateResult;
}
Here is a MCVE, but I can't replicate the problem...but maybe I'm missing some key element that would have an impact
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var lstInventoryNeedsOriginal = new List<AllocNeedVM>();
int b = 5;
for (int a = 0; a <= b; a++)
{
int c = b - a;
var allocNeed = new AllocNeedVM()
{
ItemId = c,
ItemDesc = "Item " + c.ToString(),
IsRemnant = false
};
lstInventoryNeedsOriginal.Add(allocNeed);
}
AllocNeedVM inventoryNeed;
Console.WriteLine("List before sort.");
for (int i = 0; i < lstInventoryNeedsOriginal.Count; i++)
{
inventoryNeed = lstInventoryNeedsOriginal[i];
Console.WriteLine("{0}, {1}, {2}", inventoryNeed.ItemId, inventoryNeed.ItemDesc, inventoryNeed.IsRemnant);
}
var lstInventoryNeeds = lstInventoryNeedsOriginal
.OrderBy(x => x.ItemId)
.ThenByDescending(x => x.IsRemnant)
.ToList();
Console.WriteLine("List after sort.");
for (int i = 0; i < lstInventoryNeeds.Count; i++)
{
inventoryNeed = lstInventoryNeeds[i];
Console.WriteLine("{0}, {1}, {2}", inventoryNeed.ItemId, inventoryNeed.ItemDesc, inventoryNeed.IsRemnant);
}
Console.ReadLine();
}
}
public class AllocNeedVM
{
public int ItemId { get; set; }
public string ItemDesc { get; set; }
public bool IsRemnant { get; set; }
}
}

C# merge field values if the ID's match

How do i match 2 objects in a list if their ID match, and their text doesn't?
my objects is added to a list:
List<MyObject> list = New List<MyObject>();
This could be my list (This is an object):
ID Text
1 this is some text
2 text1
1 more text
1 a little more
2 text 2
3 XXX
Then i would like the result to be:
ID Text
1 this is some text more text a little more
2 text1 text2
3 XXX
I've tried with a for in a for loop, but i just can figure it out..
for (int i = 0; i < OrderList.Count; i++)
{
bool existsMoreThanOnce = false;
for (int j = i; j < OrderList.Count; j++)
{
duplicates.Add(OrderList[i]);
if (OrderList[i].OrderNumber == OrderList[j].OrderNumber && OrderList[i].OrderText != OrderList[j].OrderText)
{
if(!uniques.Contains(OrderList[j]))
{
duplicates.Add(OrderList[j]);
existsMoreThanOnce = true;
}
}
}
if (existsMoreThanOnce == false)
{
uniques.Add(OrderList[i]);
}
}
First I create a class
public class Items
{
public int ID { get; set; }
public string Text { get; set; }
public Items(int id, string text)
{
ID = id;
Text = text;
}
}
Now I the logic of my code is
List<Items> objItems = new List<Items>();
objItems.Add(new Items(1,"Rahul"));
objItems.Add(new Items(2, "Rohit"));
objItems.Add(new Items(1, "Kumar"));
objItems.Add(new Items(2, "Verma"));
List<Items> objNew = new List<Items>(); //it will hold result
string str = "";
for (int i = 0; i < objItems.Count; i++)
{
if (objItems[i].ID > 0)
{
str = objItems[i].Text;
for (int j = i + 1; j < objItems.Count; j++)
{
if (objItems[i].ID == objItems[j].ID)
{
str += objItems[j].Text + " ";
objItems[j].ID = -1;
}
}
objNew.Add(new Items(objItems[i].ID, str));
}
}
ObjNew object contains the required output.
var result = list1.Concat(list2)
.GroupBy(x => x.ID)
.Where(g => g.GroupBy(x=>x.Text).Count() > 1)
.Select(x => x.Key)
.ToList();
You can start with LINQ's GroupBy.
var output = input.GroupBy(i => i.ID)
.Select(i => new { ID = i.Key,
Text = String.Join(" ",
i.Select(x => x.Text).ToArray()) });
First i created a class to hold your list
public class MyObject
{
public int ID { get; set; }
public string Text { get; set; }
}
then i inserted the dummy values into it
List<MyObject> obj = new List<MyObject>
{
new MyObject{ID=1, Text="this is some text"},
new MyObject{ID=2, Text="text1"},
new MyObject{ID=1, Text="more text"},
new MyObject{ID=1, Text="a little more"},
new MyObject{ID=2, Text="text 2"},
new MyObject{ID=3, Text="XXX"}
};
List<MyObject> obj2 = new List<MyObject>(); //this list will hold your output
//the linq query will filter out the uniques ids.
var uniqueIds = (from a in obj select new { a.ID, a.Text }).GroupBy(x => x.ID).ToList();
//then iterated through all the unique ids to merge the texts and list them under the unique ids.
int id=0;
foreach (var item in uniqueIds)
{
string contText = "";
for (int j = 0; j < item.Count(); j++)
{
contText += item.ElementAt(j).Text + " ";
id = item.ElementAt(j).ID;
}
obj2.Add(new MyObject { ID = id, Text = contText });
}
the list obj2 will have your desired output.

remove list-items with Linq when List.property = myValue

I have the following code:
List<ProductGroupProductData> productGroupProductDataList = FillMyList();
string[] excludeProductIDs = { "871236", "283462", "897264" };
int count = productGroupProductDataList.Count;
for (int removeItemIndex = 0; removeItemIndex < count; removeItemIndex++)
{
if (excludeProductIDs.Contains(productGroupProductDataList[removeItemIndex].ProductId))
{
productGroupProductDataList.RemoveAt(removeItemIndex);
count--;
}
}
Now i want to do the same with linq. Is there any way for this?
The second thing would be, to edit each List-Item property with linq.
you could use RemoveAll.
Example:
//create a list of 5 products with ids from 1 to 5
List<Product> products = Enumerable.Range(1,5)
.Select(c => new Product(c, c.ToString()))
.ToList();
//remove products 1, 2, 3
products.RemoveAll(p => p.id <=3);
where
// our product class
public sealed class Product {
public int id {get;private set;}
public string name {get; private set;}
public Product(int id, string name)
{
this.id=id;
this.name=name;
}
}
Firstly corrected version of your current code that won't skip entries
List<ProductGroupProductData> productGroupProductDataList = FillMyList();
string[] excludeProductIDs = { "871236", "283462", "897264" };
int count = productGroupProductDataList.Count;
for (int removeItemIndex = 0; removeItemIndex < count; removeItemIndex++)
{
while (removeItemIndex < count && excludeProductIDs.Contains(productGroupProductDataList[removeItemIndex].ProductId)) {
productGroupProductDataList.RemoveAt(removeItemIndex);
count--;
}
}
}
This linq code would do the job.
List<ProductGroupProductData> productGroupProductDataList = FillMyList();
string[] excludeProductIDs = { "871236", "283462", "897264" };
productGroupProductDataList=productGroupProductDataList.Where(x=>!excludedProductIDs.Contains(x.ProductId)).ToList();
Alternatively using paolo's answer of remove all the last line would be would be
productGroupProductDataList.RemoveAll(p=>excludedProductIDs.Contains(p=>p.ProductId));
What you mean by "The second thing would be, to edit each List-Item property with linq."?
As per your comment here's a version that creates a set that excludes the elements rather than removing them from the original list.
var newSet = from p in productGroupProductDataList
where !excludeProductIDs.Contains(p.ProductId))
select p;
The type of newSet is IEnumerable if you need (I)List you can easily get that:
var newList = newSet.ToList();

Categories