For-loop within "new"-operator - c#

I am looking for a way to interactively create a List-Object with a flexible number of elements. At the moment, i have a fixed number of elements; i can create the Object like this:
private List<DropItem> _items = new()
{
new DropItem(){ Name = "Cam 0", Identifier = "Drop Zone 1", cam_idx = 0 },
new DropItem(){ Name = "Cam 1", Identifier = "Drop Zone 1", cam_idx = 1 },
new DropItem(){ Name = "Cam 2", Identifier = "Drop Zone 1", cam_idx = 2 },
new DropItem(){ Name = "Cam 3", Identifier = "Drop Zone 1", cam_idx = 3 },
new DropItem(){ Name = "Cam 4", Identifier = "Drop Zone 2", cam_idx = 4 },
new DropItem(){ Name = "Cam 5", Identifier = "Drop Zone 2", cam_idx = 5 },
new DropItem(){ Name = "Cam 6", Identifier = "Drop Zone 2", cam_idx = 6 },
new DropItem(){ Name = "Cam 7", Identifier = "Drop Zone 2", cam_idx = 7 },
};
Is there a way to directly construct such an object with an abitrary number of elements? (a trivial attempt to write a loop within the new() - construction did unsurprisingly not work)

Enumerable.Range() should help you here
int count = 10;
List<DropItem> _items = Enumerable.Range(0, count).Select(x => new DropItem()
{
Name = "Cam " + x,
Identifier = "Drop Zone" + ((x < count / 2) ? 1 : 2),
cam_idx = x
}).ToList();
https://dotnetfiddle.net/ePpi5T

Use linq and Enumerable for this purpose:
Enumerable.Range(1, 12) // 12 cand be other number
.Select(x =>
new DropItem
{
Name = "Cam " + x,
cam_idx = x
... // whatever you want
}).ToList();

int itemsCount = 8;
for (int i = 0; i < itemsCount; i++)
{
_items.Add(new DropItem() { Name = $"Cam {i}", Identifier = $"Drop Zone {i / 4 + 1}", cam_idx = i});
}

Related

Grouping By bool produces wrong result

I am trying to visualize data from database in charts using LiveCharts library. I have managed to get some of them working fine. However I am having hard times with PieCharts. I want to simply display data in two slices. For that matter I have column in DB with name AppIsRunning.
I have made a simple example that is working fine and displaying data in slices as expected:
List<DataModel> records = new List<DataModel>();
records.Add(new DataModel { Id = 1, Revenue = 43, Name = "Item 1", AppIsRunning = true });
records.Add(new DataModel { Id = 2, Revenue = 23, Name = "Item 2", AppIsRunning = true });
records.Add(new DataModel { Id = 3, Revenue = 13, Name = "Item 3", AppIsRunning = true });
records.Add(new DataModel { Id = 4, Revenue = 87, Name = "Item 4", AppIsRunning = true });
records.Add(new DataModel { Id = 5, Revenue = 23, Name = "Item 5", AppIsRunning = true });
IEnumerable<ISeries> result1 = records.Select(x =>
new PieSeries<double>
{
Values = new List<double> { x.Revenue },
Name = x.Name,
});
Now I would like to group data to display only 2 slices AppIsRunning true/false and for Values have Revenue. So I have ended up with this code:
List<DataModel> records = new List<DataModel>();
records.Add(new DataModel { Id = 1, Revenue = 43, Name = "Item 1", AppIsRunning = true });
records.Add(new DataModel { Id = 2, Revenue = 23, Name = "Item 2", AppIsRunning = true });
records.Add(new DataModel { Id = 3, Revenue = 13, Name = "Item 3", AppIsRunning = false });
records.Add(new DataModel { Id = 4, Revenue = 87, Name = "Item 4", AppIsRunning = true });
records.Add(new DataModel { Id = 5, Revenue = 23, Name = "Item 5", AppIsRunning = true });
IEnumerable<ISeries> result1 = records
.GroupBy(g => g.AppIsRunning)
.Select(item => new PieSeries<double>
{
Values = item.Select(x => Convert.ToDouble(x.Revenue)),
Name = item.Key ? "Running" : "Not running",
});
However this makes AppIsRunning true section divided into 4 sub-slices = 4 AppIsRunning TRUE values. Please see screenshot with 4 sub-sections in blue slice:
My question is how to get rid of these 4 sub-sections and group that data into one? I need only 2 slices, no need to divide slices into sub-sections.
Here is original example from LiveCharts:
this.ActivityChartSeries = new ISeries[]
{
new PieSeries<double> { Values = new double[] { 2 }, Name = "Section 1"},
new PieSeries<double> { Values = new double[] { 21 }, Name = "Section 2"},
new PieSeries<double> { Values = new double[] { 28 }, Name = "Section 3"},
new PieSeries<double> { Values = new double[] { 2 }, Name = "Section 4"},
new PieSeries<double> { Values = new double[] { 52 }, Name = "Section 5"},
};
https://github.com/beto-rodriguez/LiveCharts2/blob/master/samples/ViewModelsSamples/Pies/Basic/ViewModel.cs
Judging from the example, you want to see a breakdown of total (sum of) revenue as a single value for each status. To do that, you could do:
IEnumerable<ISeries> result1 = records
.GroupBy(g => g.AppIsRunning)
.Select(item => new PieSeries<double>
{
Values = new List<double> {item.Sum(x => Convert.ToDouble(x.Revenue))},
Name = item.Key ? "Running" : "Not running",
});

How to make one collection using three collections?

I want to make one collection of three according to certain conditions (with LINQ). let's say I have a Class:
class Collection
{
public int id;
public string name;
public double weight;
}
Then I'm creating collections:
List<Collection> collection1 = new()
{
new Collection() { id = 11, name = "Abraham 1", weight = 1.1 },
new Collection() { id = 12, name = "Abraham 2", weight = 1.2 },
new Collection() { id = 13, name = "Abraham 3", weight = 1.3 },
};
List<Collection> collection2 = new()
{
new Collection() { id = 21, name = "Bill 1", weight = 2.1 },
new Collection() { id = 22, name = "Bill 2", weight = 2.2 },
new Collection() { id = 23, name = "Bill 3", weight = 2.3 },
};
List<Collection> collection3 = new()
{
new Collection() { id = 31, name = "David 1", weight = 3.1 },
new Collection() { id = 32, name = "David 2", weight = 3.2 },
new Collection() { id = 33, name = "David 3", weight = 3.3 },
};
TODO 1. Condition: get 1st column from 1st collection, 2nd column from 2nd collection, 3rd column from 3rd column. result should be:
{
new Collection() { id = 11, name = "Bill 1", weight = 3.1 },
new Collection() { id = 12, name = "Bill 2", weight = 3.2 },
new Collection() { id = 13, name = "Bill 3", weight = 3.1 }
}
TODO 2. Second case condition: get first elements from columns of all collections. result should be:
{
new Collection() { id = 11, name = "Abraham 1", weight = 1.1 },
new Collection() { id = 21, name = "Bill 1", weight = 2.1 },
new Collection() { id = 31, name = "David 1", weight = 3.1 }
}
Please help.
Using C# 10 and .Net 6, just use Enumerable.Zip:
var todo1 = collection1.Zip(collection2, collection3)
.Select(t => new Collection { id = t.First.id, name = t.Second.name, weight = t.Third.weight })
.ToList();
var todo2 = collection1.Zip(collection2, collection3)
.Select(t => new List<Collection> { t.First, t.Second, t.Third })
.First();
If you don't have the three argument Zip that returns a tuple, just roll your own, e.g. for Todo1:
var td1 = collection1.Zip(collection2, (c1, c2) => (c1, c2))
.Zip(collection3, (t12, c3) => (First: t12.c1, Second: t12.c2, Third: c3))
.Select(t => new Collection { id = t.First.id, name = t.Second.name, weight = t.Third.weight })
.ToList();
If you want to merge multiple collections into one collection using LINQ then do this:
List<Collection> result = collection1.Concat(collection2).OrderBy(x => x.id).ToList();

LINQ: Get an item that is not in the records - C#

This is an extended question back from my POST
class EmployeeSchedule
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime WorkDate { get; set; }
public bool isOff { get; set; }
}
Now in this case i would like to find an Id whose WorkDate is not found in 4/12/2016 from the List as my criteria.
List<Staff> workers = new List<Staff>()
{
new Staff { Id = 1, Name = "Emp 1", WorkDate = Convert.ToDateTime("4/11/2016"), IsOff = false},
new Staff { Id = 1, Name = "Emp 1", WorkDate = Convert.ToDateTime("4/12/2016"), IsOff = false},
new Staff { Id = 1, Name = "Emp 1", WorkDate = Convert.ToDateTime("4/13/2016"), IsOff = false},
new Staff { Id = 1, Name = "Emp 1", WorkDate = Convert.ToDateTime("4/14/2016"), IsOff = false},
new Staff { Id = 1, Name = "Emp 1", WorkDate = Convert.ToDateTime("4/15/2016"), IsOff = false},
new Staff { Id = 1, Name = "Emp 1", WorkDate = Convert.ToDateTime("4/16/2016"), IsOff = false},
new Staff { Id = 1, Name = "Emp 1", WorkDate = Convert.ToDateTime("4/17/2016"), IsOff = false},
new Staff { Id = 2, Name = "Emp 2", WorkDate = Convert.ToDateTime("4/11/2016"), IsOff = false},
// new Staff { Id = 2, Name = "Emp 2", WorkDate = Convert.ToDateTime("4/12/2016"), IsOff = false},
new Staff { Id = 2, Name = "Emp 2", WorkDate = Convert.ToDateTime("4/13/2016"), IsOff = false},
new Staff { Id = 2, Name = "Emp 2", WorkDate = Convert.ToDateTime("4/14/2016"), IsOff = false},
new Staff { Id = 2, Name = "Emp 2", WorkDate = Convert.ToDateTime("4/15/2016"), IsOff = false},
new Staff { Id = 2, Name = "Emp 2", WorkDate = Convert.ToDateTime("4/16/2016"), IsOff = false},
new Staff { Id = 2, Name = "Emp 2", WorkDate = Convert.ToDateTime("4/17/2016"), IsOff = false},
};
So far i have tried this code:
var notInDate = workers.Where(x => !workers.Any(y => Convert.ToDateTime("4/12/2016") != x.WorkDate));
foreach (var item in notInDate)
{
Console.WriteLine(item.Id + " " + item.WorkDate.Date);
}
Which has the output of: ID :1 WorkDate: 4/12/2016 12:00:00 AM
And this one with this output:
var notInDate = workers.Where(x => workers.Any(y => Convert.ToDateTime("4/12/2016") != x.WorkDate));
ID :1 WorkDate: 4/11/2016 12:00:00 AM
ID :1 WorkDate: 4/13/2016 12:00:00 AM
ID :1 WorkDate: 4/14/2016 12:00:00 AM
ID :1 WorkDate: 4/15/2016 12:00:00 AM
ID :1 WorkDate: 4/16/2016 12:00:00 AM
ID :1 WorkDate: 4/17/2016 12:00:00 AM
ID :2 WorkDate: 4/11/2016 12:00:00 AM
ID :2 WorkDate: 4/13/2016 12:00:00 AM
ID :2 WorkDate: 4/14/2016 12:00:00 AM
ID :2 WorkDate: 4/15/2016 12:00:00 AM
ID :2 WorkDate: 4/16/2016 12:00:00 AM
ID :2 WorkDate: 4/17/2016 12:00:00 AM
The second example just removed the record whose WorkDate is 4/12/2016
I would like to get whose Id = 2 because that Id do not have the WorkDate of 4/12/2016
If you want all entries from people that did not work on that day, you need to group by id (to get a group per person) and filter on individual groups that don't contain the date. Something like;
var notInDate =
// Create a group per employee
workers.GroupBy (x => x.Id)
// Keep only groups that didn't work
.Where (x => !x.Any (y => y.WorkDate == Convert.ToDateTime ("4/12/2016")))
// Get only the key for each group
.Select (x => x.Key);
You could do this, use GroupBy to group on Id and then validate whether staff worked on that date using Any extension..
var notInDate = workers.GroupBy(g=>g.Id)
.Where(x => !x.Any(y => DateTime.ParseExact("4/12/2016","M/d/yyyy", null) == y.WorkDate))
.FirstOrDefault(); // Get the first matching staff.
if(notInDate != null)
{
id = notInDate.Key; // StaffId
}

How can I insert a sequential number into a field using LINQ?

I have the following code that I am using to put data into a relational table using Entity Framework:
public IList<Objective> createObjectives()
{
var objectiveNames = new[]
{
"Objective 1",
"Objective 2",
"Objective 3",
"Objective 4",
"Objective 5",
"Objective 6",
"Objective 7",
"Objective 8"
};
var objectives = objectiveNames.Select(o => new Objective
{
ObjectiveSeq = ??,
Name = o,
Description = o + " Description",
ModifiedDate = DateTime.Now
}
);
return objectives.ToList();
}
I have a new field in my table name ObjectiveSeq. How can I modify my LINQ to insert a sequential number in that field starting from 1.
var objectives = objectiveNames.Select((o, index) => new Objective
{
ObjectiveSeq = index,
Name = o,
Description = o + " Description",
ModifiedDate = DateTime.Now
}
);
There's an overload to the Select function. You can find it here.
Enumerable.Select<TSource, TResult> Method (IEnumerable<TSource>, Func<TSource, Int32, TResult>)
Take a look at the sample shown in the page.
string[] fruits = { "apple", "banana", "mango", "orange", "passionfruit", "grape" };
var query = fruits.Select((fruit, index) => new { index, str = fruit });
foreach (var obj in query)
{
Console.WriteLine("{0}", obj);
}
You can see that we are using (fruit, index). You are selecting both the element and an index.
The output will be
{ index = 0, str = apple }
{ index = 1, str = banana }
{ index = 2, str = mango }
{ index = 3, str = orange }
{ index = 4, str = passionfruit }
{ index = 5, str = grape }

Sorting an IEnumerable in LINQ

How to sort the given examples.
IEnumerable<extra> eList = new List<extra>()
{
new extra{ id = 1, text = "a"},
new extra{ id = 2, text = "g"},
new extra{ id = 3, text = "i"},
new extra{ id = 4, text = "e"},
new extra{ id = 5, text = "f"},
new extra{ id = 6, text = "d"},
new extra{ id = 7, text = "c"},
new extra{ id = 8, text = "h"},
new extra{ id = 9, text = "b"}
};
IEnumerable<sample> sam = new List<sample>()
{
new sample{ id = 1, name = "sample 1", list = new List<int>{1,5,6}},
new sample{ id = 2, name = "sample 1", list = new List<int>{2,9}},
new sample{ id = 3, name = "sample 1", list = new List<int>{8,3,7}},
new sample{ id = 4, name = "sample 1", list = new List<int>{3,4,8}},
new sample{ id = 5, name = "sample 1", list = new List<int>{1,5,7}},
new sample{ id = 6, name = "sample 1", list = new List<int>{6,9,7}}
};
I have this code to sort and join the sample list to the extra object above.
var s2 = (from d1 in sam
select new
{
name = d1.name,
id = d1.id,
list =
(
from d2 in d1.list
join e in eList on d2 equals e.id
select new {
id = d2, text = e.text
}
).OrderBy(item => item.text.FirstOrDefault())
});
The code above works fine, it joined the two data and sorted the values for the list. But what I want is the output above 's2' will be sorted again by its 'list' value by 'list.text'.
So possible output above must be:
{ id = 1, name = "sample 1", list = {'a','f','d'}},
{ id = 5, name = "sample 1", list = {'a','f','c'}},
{ id = 2, name = "sample 1", list = {'g','b'}},
{ id = 4, name = "sample 1", list = {'i','e','h'}},
{ id = 6, name = "sample 1", list = {'d','b','c'}},
{ id = 3, name = "sample 1", list = {'h','i','c'}},
Is this possible in LINQ?
thanks
var newsam = sam.Select(s => new
{
id = s.id,
name = s.name,
list = s.list
.Select(l => eList.FirstOrDefault(e => e.id == l).text)
.OrderBy(e => e)
.ToList()
}
).OrderBy(s => s.list.FirstOrDefault())
.ToList();
EDIT
So, the inner lists are sorted by the text value of the eList; and the outer list is sorted by the first element of the inner list
EDIT
var s2=(from d1 in sam
select new
{
name = d1.name,
id = d1.id,
list =
(
from d2 in d1.list
join e in eList on d2 equals e.id
select e.text
).OrderBy(item => item).ToList()
}).OrderBy(item => item.list.FirstOrDefault());

Categories