Order a List a specific way - c#

I have a problem. I have a list with 4 coins and 4 values. Now the List is sorted by name like this:
1. BTC---Value
2. ETH---Value
3. LTC---Value
4. USDT---Value
But now I want to get a List with only 2 coins left:
The last coin needs to be USDT and the first Coin is the coin with the highest value. So for example if all the coins have value 3 and BTC has value 4, then I want a List like this:
1. BTC---Value
2. USDT---Value
How can I do that, because I know how to sort by value, but not with all my preferences....
Can someone help me?
DETAILS
Even if USDT has the highest value, I want that coin at the last place. If you add another coin, it needs to just look at the highest value again (except for USDT) and place that coin at the top with USDT on second place!

Updated code based on comment by DubDub.
var intermediate = list.OrderBy(x=> x.Name=="USDT").ThenByDescending(x=>x.Value);
var result = new []{intermediate.First(),intermediate.Last()};
Example,
Scenario 1 : When there are more than 2 items
var list = new List<Coin>
{
new Coin{Name="USDT", Value = 29},
new Coin{Name="ETH", Value = 13},
new Coin{Name="LTC", Value = 21},
new Coin{Name="BTC", Value = 3},
};
Output
Scenario 2 : When there are only two items
var list = new List<Coin>
{
new Coin{Name="USDT", Value = 29},
new Coin{Name="LTC", Value = 21},
};

You could do it with Linq. This wouldn't modify the list; it would create a new enumerable sorted by your criteria.
var sortedCoins = coins.OrderBy(c => c.Name == "USDT")
.ThenByDescending(c => c.Value);

Using the following stolen class from a previous answer that is now gone so I'm not sure who to give credit to, but you should be able to do the following.
Coin class
public class Coin
{
public string Name { get; set; }
public double Value { get; set; }
}
Actual code to handle list
List<Coin> list = new List<Coin>
{
new Coin{Name="USDT", Value = 29},
new Coin{Name="ETH", Value = 13},
new Coin{Name="LTC", Value = 21},
new Coin{Name="BTC", Value = 3},
};
// Take out USDT coin
Coin USDTcoin = list.Find(coin => coin.Name == "USDT");
list.RemoveAt(list.FindIndex(coin => coin.Name == "USDT"));
// Order list
list = list.OrderByDescending(coin => coin.Value).ToList();
// Make new list
list = new List<Coin>
{
list[0],
USDTcoin
};

This way:
class Program
{
static void Main(string[] args)
{
List<Coin> coins = new List<Coin>()
{
new Coin ("BTC", 1),
new Coin ("ETH", 2),
new Coin ("LTC", 3),
new Coin ("USDT", 4),
};
Coin usdt = coins.First(x => x.Name == "USDT");
coins.Remove(usdt);
coins = new List<Coin>() { coins.OrderByDescending(x => x.Value).First(), usdt };
}
}
public class Coin
{
public string Name { get; set; }
public double Value { get; set; }
public Coin(string name, double value)
{
Name = name;
Value = value;
}
}

Related

List of numbers lowest value first, and then by input date

So I have a list of prices from a database. I would like to sort it so that the first entry in a list is the entry with the lowest number. And then all other entry are order by input date.
How can this be done?
This is my code, which is a mess, sorry I'm trying stuff :)
var itemPriceDate = itemPrice.OrderBy(d => d.Invoice.DateInvoice).ToList();
var itemPriceDateLow= itemPriceDate.OrderBy(c => c.qtPrice).ThenBy(d => d.Invoice.DateInvoice);
ViewBag.ItemPrice = itemPriceDateLow; ```
First find out the lowest value from the List(itemPrice).
double lowest_price = itemPrice.Min(c => c.qtPrice);
Next, remove the lowest element from the list.
var itemToRemove = itemPrice.Single(c => c.qtPrice == lowest_price);
itemPrice.Remove(itemToRemove);
Next, sort the remaining list based on input Date.
var newList = itemPrice.OrderByDescending(d => d.Invoice.DateInvoice).ToList();
Finally, add lowest element at first index
newList.Insert(0, lowest_price);
LINQ is great when it works, but it sometimes does unexpected things. Depending on how large your dataset is, you may be better off doing it as a stored procedure that returns the data already ordered.
If the dataset is small or you're cornered into using C# to do it there is the option of using a custom sort function. Without knowing the exact structure of your data, this is more intended as a blanket example that will need tweaking accordingly.
Let's say your list is stored in the itemPrice variable, if you do something along the lines of:
itemPrice.Sort((a, b) => {
int retVal = a.qtPrice < b.qtPrice;
return ret != 0 ? ret : a.Invoice.DateInvoice < b.Invoice.DateInvoice;
});
Will sort by qtPrice and then fall back to the DateInvoice field; you may need to swap the less than to a greater than to get your desired order.
One sort is enough. What I think it should be is:
var itemPriceDateLow= itemPriceDate.OrderBy(c => c.qtPrice).ThenBy(d => d.Invoice.DateInvoice);
This will obviously give you whole collection. You might want to use .First() if you want to get top most element.
One thing to remember - ThenBy, OrderBy are ascending by default.
Take a look at this example:
class Program
{
static void Main(string[] args)
{
List<ItemPrice> items = new List<ItemPrice>();
items.Add(new ItemPrice() { Date = DateTime.Now, QtyPrice = 1});
items.Add(new ItemPrice() { Date = DateTime.Now.AddDays(-1), QtyPrice = 1});
items.Add(new ItemPrice() { Date = DateTime.Now, QtyPrice = 2});
var sortedItem = items.OrderBy(p => p.QtyPrice).ThenBy(p => p.Date).First();
Console.WriteLine($"Default Ascending sort {sortedItem.Date}, {sortedItem.QtyPrice}");
var sortedItemWithReverseDate = items.OrderBy(p => p.QtyPrice).ThenByDescending(p => p.Date).First();
Console.WriteLine($"Descending sort on date {sortedItemWithReverseDate.Date}, {sortedItemWithReverseDate.QtyPrice}");
}
}
class ItemPrice {
public DateTime Date { get; set; }
public decimal QtyPrice { get; set; }
}
It will give you:
Default Ascending sort 16/08/2021 12:47:34, 1
Descending sort on date 17/08/2021 12:47:34, 1
You would need to iterate the collection twice in this case, since you would first need to know the Aggregate Value (Min). Then, you could use a Custom Comparer as the following.
public class CustomComparer : IComparer<Item>
{
private int _minValue;
public CustomComparer(int minValue)
{
_minValue= minValue;
}
public int Compare(Item instanceA, Item instanceB)
{
if(instanceA.Price == _minValue) return -1;
if(instanceB.Price == _minValue) return 1;
return instanceA.InputDate.CompareTo(instanceB.InputDate);
}
}
Now you can fetch the result as
var min = list.Min(x=>x.Price);
var result = list.OrderBy(x=>x,new CustomComparer(min));
Example,
public class Item
{
public int Price{get;set;}
public DateTime InputDate{get;set;}
}
var list = new List<Item>
{
new Item{Price = 2, InputDate=new DateTime(2021,3,1)},
new Item{Price = 12, InputDate=new DateTime(2021,7,1)},
new Item{Price = 12, InputDate=new DateTime(2021,9,1)},
new Item{Price = 42, InputDate=new DateTime(2021,1,1)},
new Item{Price = 32, InputDate=new DateTime(2021,6,1)},
new Item{Price = 22, InputDate=new DateTime(2021,4,1)},
new Item{Price = 2, InputDate=new DateTime(2021,3,2)},
new Item{Price = 12, InputDate=new DateTime(2021,2,1)}
};
var min = list.Min(x=>x.Price);
var result = list.OrderBy(x=>x,new CustomComparer(min));
Output
Thx for all your inputs.
For me the right way to go was.
Order my "itemPrice" list by "OrderByDescending(by date)"
Then find out the lowest value from the List(itemPrice).
double lowest_price = itemPrice.Min(c => c.qtPrice);
Then declare a new List
List<qtInvoice> newItemPrice = new List<qtInvoice>();
First loop that adds all the "lowest_price" to "newItemPrice" list
foreach (var item in itemPriceDate)
{
if (item.qtPrice == lowest_price)
{
newItemPrice.Add(item);
}
}
Then in second loop you add all the rest of the prices to "newItemPrice" list
foreach (var item in itemPriceDate)
{
if (item.qtPrice != lowest_price)
{
newItemPrice.Add(item);
}
}

How to dynamically group a list and select values using linq query in C#?

I have an input list test
class Tracker
{
public string Toolid {get;set;}
public string Description {get;set;}
public int length {get;set;}
public int breadth {get;set;}
public int height {get;set;}
}
List<Tracker> test = new List<Tracker>();
test.Add( new Tracker {Toolid="A.1",Description ="ABC",length = 10, breadth =10,height = 50});
test.Add( new Tracker {Toolid="A.1",Description ="ABC", length = 10, breadth =10,height = 50});
test.Add( new Tracker {Toolid="C.1",Description ="ABCD", length = 10, breadth =10,height = 50});
test.Add( new Tracker {Toolid="D.1",Description ="Admin123", length = 10, breadth =10,height = 50});
This list contain more values like weight, colour etc.
For better understanding I have added only 5 member variables in the class Tracker.
I need to Group the list test based on the values of another list (grpList).
This list (grpList ) is dynamic, hence the number of parameter and values in the list may change.
So I need a dynamic Group By of list using LINQ query.
case 1 : sometimes this list grpList contain 2 values .
List <string> grpList = new List<string>();
grpList.Add(ToolId);
grpList.Add(Description);
if So , I have to group the list test with ToolId and Description,
case 2 : if list grpList contain N values , I have to group the list test with 'N' values .
The number of values in the grpList varies. I have group the main list test using the values in grpList. If grpList contain 2 values ,group the test list with 2 values . if If grpList contain 5 values , group the test list with 5 values.
NB : I need to Group the list test ( Main list).
grpList values only for grouping .
try reflection:
List<string> grpList = new List<string>();
grpList.Add("Toolid");
grpList.Add("Description");
var groups = new Dictionary<string, IEnumerable>();
var all_properties = typeof(Tracker).GetProperties();
foreach ( var prop_name in grpList )
{
var prop = all_properties.First( x => x.Name == prop_name);
var group = test.GroupBy( x => prop.GetValue( x ) );
groups.Add( prop_name, group );
}
if you want to have an sql-like nested grouping, apply the GroupBy to the resulting groups:
var groups = new List<List<Tracker>>() { test };
foreach ( var prop_name in grpList )
{
var prop = all_properties.First( x => x.Name == prop_name);
var newgroups = new List<List<Tracker>>();
foreach ( var group in groups)
{
var subgroups = group.GroupBy( x => prop.GetValue( x ) );
newgroups.AddRange( subgroups.Select(g => g.ToList()).ToList() );
}
groups = newgroups;
}
I used Enumerable.GroupBy Method selector predicate.
Here's how I generated the predicate and the solution seems to work.
public class Tracker
{
public string Toolid { get; set; }
public string Description { get; set; }
public int length { get; set; }
public int breadth { get; set; }
public int height { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Tracker> test = new List<Tracker>();
test.Add(new Tracker { Toolid = "A.1", Description = "ABC", length = 50, breadth = 10, height = 50 });
test.Add(new Tracker { Toolid = "A.1", Description = "ABC", length = 20, breadth = 10, height = 50 });
test.Add(new Tracker { Toolid = "C.1", Description = "LMN", length = 10, breadth = 10, height = 50 });
test.Add(new Tracker { Toolid = "D.1", Description = "Admin123", length = 7, breadth = 10, height = 50 });
List<string> grpList = new List<string>();
grpList.Add("length");
grpList.Add("Description");
var sourceParm = Expression.Parameter(typeof(Tracker), "x");
List<Expression> propertyExpressions = new List<Expression>();
foreach (var f in grpList.ToArray())
{
Expression conv = Expression.Convert(Expression.Property(sourceParm, f), typeof(object));
propertyExpressions.Add(conv);
}
var concatMethod = typeof(string).GetMethod(
"Concat",
new[] { typeof(object), typeof(object), typeof(object) });
Expression body = propertyExpressions.Aggregate((x, y) => Expression.Call(concatMethod,
x,
Expression.Constant(","),
y));
var groupSelector = Expression.Lambda<Func<Tracker, string>>(body, sourceParm);
var j = test.GroupBy(groupSelector.Compile());
}

How to combine elements of list? (C#)

I am working in C#.
How to combine (sum, plus, minus) these class elements in both lists?
class Attribute
{
public AttributeType WhatAttri;
public float amount;
}
enum AttributeType{
maxhp, str, dex, int, wis,,,,
}
Attribute[] attList1;
Attribute[] attList2;
If specific values are like this,
attList1[0] = new Attribute(AttributeType.maxhp, 6)
attList1[1] = new Attribute(AttributeType.str, 4)
attList1[2] = new Attribute(AttributeType.dex, 3)
attList2[0] = new Attribute(AttributeType.str, 9)
attList2[1] = new Attribute(AttributeType.int, 7)
attList2[2] = new Attribute(AttributeType.wis, 5)
I want final result like this, (attList1 values are added, attList2 values are deducted, and also sum(or minus or plus) duplicated AttributeType)
So at above two lists, AttributeType.str is same, so deduct duplicated attList2[0]'s amount variable's value (9) from attList1[1]'s value (4)
and exclude this element from attList2.
So final result should be,
Attribute[] combinedList; (or List<Attribute> combinedList )
combinedList[0] = new Attribute(AttributeType.maxhp, 6)
combinedList[1] = new Attribute(AttributeType.str, -5) (4 - 9)
combinedList[2] = new Attribute(AttributeType.dex, 3)
combinedList[3] = new Attribute(AttributeType.int, -7)
combinedList[4] = new Attribute(AttributeType.wis, -5)
How to achieve this?
Thanks.
var result =
attList2.Select(a => new Attribute(a.WhatAttri, -a.amount)) // line 1
.Concat(attList1) // line 2
.GroupBy(a => a.WhatAttri) // line 3
.Select(g => new Attribute(g.Key, g.Sum(a => a.amount))); // line4
foreach(var a in result)
{
Console.WriteLine($"{a.WhatAttri}: {a.amount}");
}
You want to sum up the counts of the first list and subtract the amounts of the second list. So first I transform the second list to a new list with negative amounts (line 1). then the two lists are joined into one list (line 2).
Then the big line is grouped by type (line 3). and then you have a structure of Key and items, where you create new Attributes by using the key and the sum of the amounts (line 4).
Edit: replaced "Union" in line 2 by "Concat" to avoid dropping duplicate values in case there would be a custom comparer method in class Attribute
The main issue is that you didn't have positive or negative value in the attributes modifier. How whould you boost those attribute? Once it's fix the solution is easy
Add both list with concat, GroupBy AttributeType, and select the values.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var attributes = new Attribute[] {
new Attribute{WhatAttri=AttributeType.maxhp, amount=6 },
new Attribute{WhatAttri=AttributeType.str, amount=4 },
new Attribute{WhatAttri=AttributeType.dex, amount=3 },
};
//Attribute modifier has to be either positive or negative
var attributesModifier = new Attribute[] {
new Attribute{WhatAttri=AttributeType.str, amount=-9 },
new Attribute{WhatAttri=AttributeType.#int, amount=-7 },
new Attribute{WhatAttri=AttributeType.wis, amount=-5 },
};
var newAttributes = attributes
.Concat(attributesModifier)
.GroupBy(x => x.WhatAttri)
.Select(group =>
new Attribute {
WhatAttri = group.Key,
amount = group.Sum(g => g.amount)
});
newAttributes.Dump();
}
public class Attribute
{
public AttributeType WhatAttri { get; set; }
public float amount { get; set; }
}
public enum AttributeType
{
maxhp, str, dex, #int, wis
}
}
OnLine demo https://dotnetfiddle.net/3C7n7F
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public enum AttributeType
{
maxhp, str, dex, intel, wis,
}
[System.Serializable]
public class Attribute
{
public AttributeType WhatAttri;
public float amount;
public Attribute(AttributeType type, int a)
{
WhatAttri = type;
amount = a;
}
}
public class LinqTest : MonoBehaviour
{
Attribute[] attList1 = new Attribute[3];
Attribute[] attList2 = new Attribute[3];
void Start()
{
attList1[0] = new Attribute(AttributeType.maxhp, 6);
attList1[1] = new Attribute(AttributeType.str, 4);
attList1[2] = new Attribute(AttributeType.dex, 3);
attList2[0] = new Attribute(AttributeType.str, 9);
attList2[1] = new Attribute(AttributeType.intel, 7);
attList2[2] = new Attribute(AttributeType.wis, 5);
Calcul();
}
void Calcul()
{
var result = attList2
.Select(a => new Attribute(a.WhatAttri, -(int)a.amount)) // line 1
.Union(attList1) // line 2
.GroupBy(a => a.WhatAttri) // line 3
.Select(g => new Attribute(g.Key, g.Sum(a => (int)a.amount))); // line4
foreach (var a in result)
{
Debug.Log($"{a.WhatAttri}: {a.amount}");
}
}
}
This is final result of testing above code by answers.
Using Unity engine.

Differences between two models

Is there a way to compare 2 models and only show the differences, for example what has been updated, added or deleted?
For example, in the models below, I have created a number of Sample models:
var grocers1 = new List<Grocer>();
var grocer1 = new Grocer
{
Id = 1,
Expenditure = 500,
Name = "Bob"
};
grocers1.Add(grocer1);
var grocers2 = new List<Grocer>();
var grocer2 = new Grocer
{
Id = 1,
Expenditure = 300,
Name = "Bob"
};
grocers2.Add(grocer2);
var fruits = new List<Fruit>();
var fruit1 = new Fruit();
fruits.Add(fruit1);
var orders1 = new List<Order>();
var order1 = new Order
{
Id = 1,
SampleId = 1,
Fruits = fruits
};
var order2 = new Order
{
Id = 1,
SampleId = 1,
Fruits = fruits
};
orders1.Add(order1);
orders1.Add(order2);
var orders2 = new List<Models.Documents.Order> {order1};
var sample = new Sample
{
Id = 1,
Date = Convert.ToDateTime("2018-10-23"),
Grocers = grocers1,
Orders = orders1
};
var changedSample = new Sample
{
Id = 1,
Date = Convert.ToDateTime("2018-10-22"),
Grocers = grocers2,
Orders = orders1
};
var otherChangedSample = new Sample
{
Id = 1,
Date = Convert.ToDateTime("2018-10-23"),
Grocers = grocers1,
Orders = orders2
};
So if I compare sample to changedSample it should just show the Date has changed from 2018-10-23 to 2018-10-22 and that the Expenditure has changed from 500 to 300.
Then if I was to compare sample to otherChangedSample it should show that order2 has been removed.
And then finally if I was to compare otherChangedSample to sample it would show that order 2 had been added.
I have tested with AutoMapper this is great for comparing the same base model, excluding lists, it nicely highlights the changes.
I then tried Compare-Net-Objects which is good, this time does take into account lists and highlights the changes, but only if the list count stays the same. It will identify the list count change but not tell you the values of what has been removed or the values of what has been added.
Any help would be much appreciated.
You can use reflection and extension method as well:
var sample = new Sample
{
Id = 1,
Date = Convert.ToDateTime("2018-10-23"),
Grocers = grocers1,
Orders = orders1
};
var otherChangedSample = new Sample
{
Id = 1,
Date = Convert.ToDateTime("2018-10-23"),
Grocers = grocers1,
Orders = orders2
};
class Variance
{
public string Prop { get; set; }
public object valA { get; set; }
public object valB { get; set; }
}
List<Variance> rt = sample.DetailedCompare(otherChangedSample);
using System.Collections.Generic;
using System.Reflection;
static class extentions
{
public static List<Variance> DetailedCompare<T>(this T val1, T val2)
{
List<Variance> variances = new List<Variance>();
FieldInfo[] fi = val1.GetType().GetFields();
foreach (FieldInfo f in fi)
{
Variance v = new Variance();
v.Prop = f.Name;
v.valA = f.GetValue(val1);
v.valB = f.GetValue(val2);
if (!v.valA.Equals(v.valB))
variances.Add(v);
}
return variances;
}
}
have you coded your model classes yourself? If not, then you have to deal with reflection (could be slow and you have to do a lot of programming to cover all data types), or with serialization (serialize as string and do a string compare).
If you can extend your classes, then I would add a method to every single class:
internal void TrackChange(T other, List<Change> changes)
This method in your class Sampe would look like:
void TrackChange(Sample other, List<Change> changes)
{
if (this.Id != other.Id) changes.add(new Change(...));
if (this.Date != other.Date) changes.add(new Change(...));
if (this.Grocers.count != other.Grocers.count) changes.add(new Change(...)); // number of items has changed
for (int i = 0; i < math.min(this.grocers.count, other.grocers.count); i++)
this.grocers[i].TrackChange(other.grocers[i], changes);
....
}
The Grocer class has its onwn TrackChange method. and so on.
This is some coding, but the most efficient and you can handle all cases yourself. for example if the order of grocers in your list does not mind, then you can iterate all grocers of this list and try to find the corresponding grocer in the others list (e.g. by Id) and call the TrackChange then.

How to sort list of class object

I have list of model class object. class scoreboard which have total score as one property.
Scoreboard scoreboard = new Scoreboard();
I am sorting list like this
data= data.OrderByDescending(x => x.totalScore).ToList()
but wont work.
please what should I do the list is of object of class.
If I understood your sorting issue correctly, this might help
List<Class1> Scores = new List<Class1>();
Scores.Add(new Class1 { Score = 1, TotalScore = 2, User = "A" });
Scores.Add(new Class1 { Score = 1, TotalScore = 5, User = "B" });
Scores.Add(new Class1 { Score = 1, TotalScore = 3, User = "C" });
Scores = Scores.OrderByDescending(x => x.TotalScore).ToList();
This will sort it by total score.
You can sort your score if you have the data like below as one property,
List<int> scoreData = new List<int>();
scoreData.Add(300);
scoreData.Add(201);
scoreData.Add(400);
Then to sort,
var sortedData = data.Sort();

Categories

Resources