Related
This is what I'm thinking, I want a database that creates a row for each item, each row will have a set of columns, these columns might look like this
Id(pk), ItemId, itemBuyPrice, itemSellPrice
However I also want to store a collection of datapoints which resembles a datepoint object which stores things such as long dateInSeconds (epoch time), price at this point etc.
So each item will have a list of datapoints which will be used for graphs.
The issue I'm facing is that, once every day, that collection needs to update because the API updates obviously and displays the most recent date.
How would I update that list using EF Core?
This is what I have so far, I add data like this..
try
{
ctx.Items.Add(new ItemModel
{
ItemId = 2,
Buy = 100,
Sell = 100,
Datapoints = new List<DataPoints>()
{
new DataPoints { ItemId = 2, Time = 100000, Buy = 200, Sell = 200 },
new DataPoints { ItemId = 2, Time = 100000, Buy = 200, Sell = 200 }
}
});
ctx.Items.Add(new ItemModel
{
ItemId = 3,
Buy = 100,
Sell = 100,
Datapoints = new List<DataPoints>()
{
new DataPoints { ItemId = 3, Time = 100000, Buy = 200, Sell = 200 }
}
});
ctx.Items.Add(new ItemModel
{
ItemId = 4,
Buy = 100,
Sell = 100,
Datapoints = new List<DataPoints>()
{
new DataPoints { ItemId = 4, Time = 100000, Buy = 200, Sell = 200 }
}
});
ctx.SaveChanges();
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
And then I comment that out, to try to update the datapoints for each item with this code
var dp = ctx.Items.Include(x => x.Datapoints).FirstOrDefault(x => x.ItemId == item.ItemId).Datapoints;
foreach (var datapoint in dp)
{
ctx.Datapoints.Remove(datapoint);
ctx.SaveChanges();
ctx.Items.Add(new ItemModel
{
ItemId = 2,
Buy = 100,
Sell = 100,
Datapoints = new List<DataPoints>()
{
new DataPoints { ItemId = 2, Time = 200000, Buy = 210, Sell = 210 },
new DataPoints { ItemId = 2, Time = 200000, Buy = 210, Sell = 210 }
}
});
ctx.Items.Add(new ItemModel
{
ItemId = 3,
Buy = 100,
Sell = 100,
Datapoints = new List<DataPoints>()
{
new DataPoints { ItemId = 3, Time = 200000, Buy = 210, Sell = 210 }
}
});
ctx.Items.Add(new ItemModel
{
ItemId = 4,
Buy = 100,
Sell = 100,
Datapoints = new List<DataPoints>()
{
new DataPoints { ItemId = 4, Time = 200000, Buy = 210, Sell = 210 }
}
});
ctx.SaveChanges();
}
And as of right now, it throws an exception saying that the collection has been modified and it can't continue after it changes the first item.
Here is all the code
https://hatebin.com/eeldpnbeaf
You will have to get the data from the database and update it, something like this:
//
List<Datapoints> pointsToUpdate = new List<DataPoints>()
{
new DataPoints { Time = 100000, Buy = 200, Sell = 200 },
new DataPoints { Time = 100000, Buy = 200, Sell = 200 }
}
}
// You will need the id of the item for wich you want to update the Datapoints collection and the datapoits class should have the itemId
class DataPoints
{
public int Id { get; set; }
public int Time { get; set; }
public int Buy { get; set; }
public int Sell { get; set; }
public int ItemId {get;set;}
}
// update the dataPoints with the new values
// here you need a for loop to update the values
using (MyContext ctx = new MyContext()) {
foreach (var item in pointsToUpdate)
{
// get the points
var dp = ctx.Datapoints.Where(dps => dps.ItemId == item.ItemId).ToList();
foreach (var datapoint in dp)
{
// condition to make the update (if....)
if(item.ItemId == datapoint.ItemId)
{
datapoint.Time = item.Time;
datapoint.Sell = item.Sell;
datapoint.Buy = item.Buy;
ctx.Datapoints.Update(datapoint);
}//endif
}
}//end the for here
//save the changes
ctx.SaveChanges();
}
Something like this. This should work.
In my current test project I'm looking to combine all objects in a list where one of their values is the same as in another object, I would then like to check the other values under these objects and combine them together, here's and example:
Object1
{
id = 111,
price1 = 10,
price 2 = 20
}
Object2
{
id = 222,
price1 = 10,
price 2 = 20
}
Object3
{
id = 111,
price1 = 30,
price 2 = 70
}
Object4
{
id = 444,
price1 = 15,
price 2 = 25
}
From the above Object1 and and Object3 would be combined based on their related 'id' value, their prices would then be combined and would result in the following object replacing Object1 and Object3 in a list:
NewObject
{
id = 111,
price1 = 40,
price 2 = 90
}
The end list would then look like this:
NewObject
{
id = 111,
price1 = 40,
price 2 = 90
}
Object2
{
id = 222,
price1 = 10,
price 2 = 20
}
Object4
{
id = 444,
price1 = 15,
price 2 = 25
}
So far I would go about obtaining the value using linq as follows:
Select all with the same id add thier values
Create new object with combined values for all obtained in step 1 and add to new list
Continue over list and if the 'id 'already exists in new list then ignore it as it's already been combined into the new list
Is there maybe a quicker easier way with a single LINQ statement?
var result = source
.GroupBy(x => x.id,
(key, values) => new {
id = key,
price1 = values.Sum(x => x.price1),
price2 = values.Sum(x => x.price2)
});
try group by
var combined = list.GroupBy(x => x.id, x => x).Select(x => new ListObj()
{
id = x.Key,
price1 = x.Sum(s => s.price1),
price2 = x.Sum(s => s.price2),
});
whole console app:
class Program
{
static void Main(string[] args)
{
var list = new List<ListObj>()
{
new ListObj()
{
id = 111,
price1 = 10,
price2 = 20
},
new ListObj()
{
id = 222,
price1 = 10,
price2 = 20
},
new ListObj()
{
id = 111,
price1 = 30,
price2 = 70
},
new ListObj()
{
id = 444,
price1 = 15,
price2 = 25
},
};
var combined = list
.GroupBy(x => x.id, x => x)
.Select(x => new ListObj()
{
id = x.Key,
price1 = x.Sum(s => s.price1),
price2 = x.Sum(s => s.price2),
});
Console.ReadKey();
}
}
public class ListObj
{
public int id { get; set; }
public int price1 { get; set; }
public int price2 { get; set; }
}
need some help with my foreach to recongonize the empty record and fill the gap with "-";
let's say we have 30min, 45min, 90min, 120min and not 60min in the case:
It can count the total records of each id, let's say the maximum is 5, 30min, 45min, 60min, 90min and 120min.
if there are 3, then it could check which is missing and than can fill with "-".
Same ideia of the script.
List<Treatment> treatment = new List<Treatment>();
treatment.Add(new Treatment { id = 1, treatmentNameId = 11, duration = "30", price = 30 });
treatment.Add(new Treatment { id = 1, treatmentNameId = 11, duration = "45", price = 45 });
treatment.Add(new Treatment { id = 1, treatmentNameId = 11, duration = "60", price = 60 });
treatment.Add(new Treatment { id = 1, treatmentNameId = 2, duration = "30", price = 30 });
//treatment.Add(new Treatment { id = 1, treatmentNameId = 2, duration = "45", price = 45 });
treatment.Add(new Treatment { id = 1, treatmentNameId = 2, duration = "60", price = 60 });
var newList = (from t in treatment
select t)
.AsQueryable().ToList();
List<List> newList= new List<List>();
foreach (var item in newList)
{
if (item.duration == "30")
{
newList.Add(new List { treatmentNameId = item.treatmentNameId, thirtyMin = "30" });
}
if (item.duration == "45")
{
newList.Add(new List { treatmentNameId = item.treatmentNameId, fortyFive= "45" });
}
if (item.duration == "60")
{
newList.Add(new List { treatmentNameId = item.treatmentNameId, sixty= "60" });
}
}
The end result should like something as,
id:1 30, 45, 60, -
id:2 30, - , 60, 90
id:3 - , 45, -, 90
etc...
Many many thanks for the help.
I'm not going to provide a very detailed explanation of all this, but conceptually I'm just grouping by treatmentNameId and then doing something very similar to an outer join in SQL. If the group join comes up empty for the duration, I'm selecting '-' instead of the duration. Using anonymous types for brevity.
var durations = new[] {"15", "30", "45", "60"};
var results = treatments
.GroupBy(t => t.treatmentNameId).Select(group => new
{
treatmentNameId = group.Key,
durations = durations.GroupJoin(group, s => s, g => g.duration,
(s, grouping) => !grouping.Any() ? "-" : s)
});
foreach (var result in results)
{
Console.WriteLine(result.treatmentNameId + ": " + string.Join(", ", result.durations));
}
This results in:
11: -, 30, 45, 60
2: -, 30, -, 60
If you have problems with something more specific, I'd be happy to explain further.
List<object> Students = new List<object>() {
new { Name = "A", Age = 28, Height = 175 },
new { Name = "B", Age = 29, Height = 176 },
new { Name = "C", Age = 30, Height = 177 },
new { Name = "D", Age = 31, Height = 178 },
new { Name = "A", Age = 32, Height = 179 },
new { Name = "E", Age = 33, Height = 180 },
new { Name = "A", Age = 34, Height = 181 },
new { Name = "F", Age = 35, Height = 182 },
new { Name = "B", Age = 36, Height = 183 }
};
1) how can I group the above list by Age?
I tried something like var test = Students.GroupBy(x=> x.Age);, but it didn't work.
2) I want to create a dictionary which has Name as Key and Height as Value . How can I do this?
Your approach to use straight object wont work because object does not have Age property. It is better for you to use predefined class with Name, Age and Height properties. It will be much more readable and type safe...
class Student
{
public string Name {get; set;}
public int Age {get; set;}
public int Height {get; set;}
public Student() {} //you would need parameterless constructor to use "new Student {...}"
public Student(string name, int age, int height)
{
Name = name;
Age = age;
Height = height;
}
}
var Students = new List<Student>()
{
new Student { Name = "A", Age = 29, Height = 175 }, //using initializer
new Student("B", 30, 176), //using constructor
...
};
But if it is impossible - you can always use dynamic. Like this:
List<dynamic> Students = new List<dynamic>()
{
new { Name = "A", Age = 28, Height = 175 },
...
};
var test = Students.GroupBy(x=> x.Age);
and for dictionary - use ToDictionary:
var test = Students.ToDictionary(x=> x.Name, x => x.Height);
however you might want to try ToLookup because you have duplicates in your Names :)
You can do the following:
var Students = new []{
new { Name = "A", Age = 28, Height = 175 },
new { Name = "B", Age = 29, Height = 176 },
new { Name = "C", Age = 30, Height = 177 },
new { Name = "D", Age = 31, Height = 178 },
new { Name = "A", Age = 32, Height = 179 },
new { Name = "E", Age = 33, Height = 180 },
new { Name = "A", Age = 34, Height = 181 },
new { Name = "F", Age = 35, Height = 182 },
new { Name = "B", Age = 36, Height = 183 }
}.ToList();
var groupedStudents = Students.GroupBy(x=>x.Age);
The key differences are that I create an anonymous list (I don't actually need the .ToList() but I'm showing you how in case you want to do any other List stuff with it). The advantage of this is that ToList is a generic method that can infer what the type is from the enumerable it is working on which in this case is an enumerable of anonymous types.
Of course many times you might be better off just creating a Student object rather than using an anonymous one.
As for creating a dictionary with name as key and height as value - you can't. Dictionaries need to have unique keys and you have multiple items with the same name here so trying to do ToDictionary() it wouldn't work.
You could try using ToLookup() which would take a key to an enumerable of matching values (similar to group by but easier to look things up).
var data = new[] {
new { Id = 0, Cat = 1, Price = 2 },
new { Id = 1, Cat = 1, Price = 10 },
new { Id = 2, Cat = 1, Price = 30 },
new { Id = 3, Cat = 2, Price = 50 },
new { Id = 4, Cat = 2, Price = 120 },
new { Id = 5, Cat = 2, Price = 200 },
new { Id = 6, Cat = 2, Price = 1024 },
};
var ranges = new[] { 10, 50, 100, 500 };
Needed output is grouped price count by equal or greater than the range used according categories.
(in one linq statement)
cat range count
-------------------------------------
1 10 2 (In 1. categories there is 2 item that price >= 10(range) [10;30])
2 10 4 (In 2. categories there is 4 item that price >= 10(range) [50;120;200;1024])
2 50 4 ....
2 100 3 ....
2 500 1 (In 2. categories there is 1 item that price >= 500(range) [1024])
Try this:
var data = new[] {
new { Id = 0, Cat = 1, Price = 2 },
new { Id = 1, Cat = 1, Price = 10 },
new { Id = 2, Cat = 1, Price = 30 },
new { Id = 3, Cat = 2, Price = 50 },
new { Id = 4, Cat = 2, Price = 120 },
new { Id = 5, Cat = 2, Price = 200 },
new { Id = 6, Cat = 2, Price = 1024 },
};
var ranges = new[] { 10, 50, 100, 500 };
var result = from r in ranges
from g in data
where g.Price >= r
select new {g.Cat, Price=r};
var groupedData =
from d in result
group d by new{d.Cat, d.Price} into g
select new{Cat=g.Key.Cat, Price=g.Key.Price, TotalCount=g.Count()};
This should work:
var values =
data.SelectMany(x => ranges.Where(y => x.Price >= y)
.Select(y => new { Record = x, Range = y }))
.GroupBy(x => new { Cat = x.Record.Cat, Range = x.Range })
.Select(x => new { Cat = x.Key.Cat, Range = x.Key.Range, Count = x.Count()});
Results:
{ Cat = 1, Range = 10, Count = 2 }
{ Cat = 2, Range = 10, Count = 4 }
{ Cat = 2, Range = 50, Count = 4 }
{ Cat = 2, Range = 100, Count = 3 }
{ Cat = 2, Range = 500, Count = 1 }