I have a list of objects that I want to sort with multiple conditions, I was able to do it with the first two ones, but with the last condition, I'm having a hard time finding a way to achieve it.
I want to order the list of objects first by gamme
and after that by series and finally by the list of characteristics depending on a certain order that I know in advance.
In this case, I want the last sort will respect the following order, this list is dynamic so the order may change:
["largeur","longueur","epaisseur","type_pose"] by value of course.
I'm stuck right now with this:
listOfProducts.OrderBy(c => c.gamme).ThenBy(c => c.serie);
[
{
"uid": "QB32-20220621-10096",
"gamme": "ATENEA",
"serie": "ALASKA",
"caracteristiques": [
{
"nom": "type_pose",
"label": null,
"valeur": "Pose collée ou scellée selon les dispositions des textes de mise en œuvre"
},
{
"nom": "type_carreau",
"label": null,
"valeur": "Grès pressé émaillé"
},
{
"nom": "groupe_absorption",
"label": null,
"valeur": "Bla"
},
{
"nom": "usage exterieur",
"label": null,
"valeur": "Conforme"
},
{
"nom": "glissance",
"label": null,
"valeur": "Non revendiqué"
},
{
"nom": "finition",
"label": null,
"valeur": "Lisses"
},
{
"nom": "largeur",
"label": null,
"valeur": "500"
},
{
"nom": "longueur",
"label": null,
"valeur": "500"
},
{
"nom": "epaisseur",
"label": null,
"valeur": "9"
},
{
"nom": "particularite",
"label": null,
"valeur": "Non rectifié"
},
{
"nom": "part_produit",
"label": null,
"valeur": "nc"
},
{
"nom": "ref_commerciale_f",
"label": null,
"valeur": ""
},
{
"nom": "coloris_f",
"label": null,
"valeur": "Blanco"
},
{
"nom": "option",
"label": null,
"valeur": "/"
},
{
"nom": "classement",
"label": null,
"valeur": "U3 P3 E3 C2"
}
]
},
{
"uid": "QB32-20220621-10177",
"gamme": "ATENEA",
"serie": "ALOE",
"caracteristiques": [
{
"nom": "type_pose",
"label": null,
"valeur": "Pose collée ou scellée selon les dispositions des textes de mise en œuvre"
},
{
"nom": "type_carreau",
"label": null,
"valeur": "Grès pressé non émaillé"
},
{
"nom": "groupe_absorption",
"label": null,
"valeur": "Bla"
},
{
"nom": "usage exterieur",
"label": null,
"valeur": "Conforme"
},
{
"nom": "glissance",
"label": null,
"valeur": "Non revendiqué"
},
{
"nom": "finition",
"label": null,
"valeur": "Décorés"
},
{
"nom": "largeur",
"label": null,
"valeur": "607.5"
},
{
"nom": "longueur",
"label": null,
"valeur": "607.5"
},
{
"nom": "epaisseur",
"label": null,
"valeur": "9.6"
},
{
"nom": "particularite",
"label": null,
"valeur": "Non rectifié"
},
{
"nom": "part_produit",
"label": null,
"valeur": "nc"
},
{
"nom": "ref_commerciale_f",
"label": null,
"valeur": ""
},
{
"nom": "coloris_f",
"label": null,
"valeur": "Taupe"
},
{
"nom": "option",
"label": null,
"valeur": "/"
},
{
"nom": "classement",
"label": null,
"valeur": "U4 P4 E3 C2"
}
]
},
{
"uid": "QB32-20220621-10054",
"gamme": "CASA INFINITA",
"serie": "IN TIME LAPPATO",
"caracteristiques": [
{
"nom": "type_pose",
"label": null,
"valeur": "Pose collée ou scellée selon les dispositions des textes de mise en œuvre"
}
]
}
]
CodePudding user response:
You can sort by values in the sub-array like this:
listOfProducts
.OrderBy(c => c.gamme)
.ThenBy(c => c.serie)
.ThenBy(c => c.caracteristiques
.FirstOrDefault(x => x.nom == "largeur")?.valeur)
.ThenBy(c => c.caracteristiques
.FirstOrDefault(x => x.nom == "longueur")?.valeur)
.ThenBy(c => c.caracteristiques
.FirstOrDefault(x => x.nom == "epaisseur")?.valeur)
.ThenBy(c => c.caracteristiques
.FirstOrDefault(x => x.nom == "type_pose")?.valeur);
In case of a dynamic list of attributes, you can build the Linq query dynamically:
var dynamicCriteria = new string[] { "largeur","longueur","epaisseur","type_pose" };
var sorted = listOfProducts
.OrderBy(c => c.gamme)
.ThenBy(c => c.serie);
foreach (var crit in dynamicCriteria)
{
sorted = sorted.ThenBy(c => c.caracteristiques
.FirstOrDefault(x => x.nom == crit)?.valeur);
}
Above code first searches the sub-array for the item with the specific name and then returns the value of the item for sort-comparison.
Downside of this Linq-based approach is that the sub-array is searched multiple times. If you need the comparison on multiple occasions and have a lot of caracteristiques
, you could implement a custom comparer using the IComparer<T>
interface or make the class comparable by using the IComparable<T>
interface. In the Compare
or CompareTo
methods you can implement the comparison in a more efficient manner. The following sample shows a simple comparer (adjust it to your needs):
public class MyComparer : IComparer<Item>
{
private readonly IEnumerable<string> _dynamicCriteria;
public MyComparer(IEnumerable<string> dynamicCriteria)
{
_dynamicCriteria = dynamicCriteria;
}
public int Compare(Item a, Item b)
{
// Check static criteria
var result = string.Compare(a.gamme, b.gamme);
if (result != 0)
return result;
result = string.Compare(a.serie, b.serie);
if (result != 0)
return result;
// Get values for dynamic criteria
var dynamicValuesA = a.caracteristiques
.Where(x => _dynamicCriteria.Contains(x.nom))
.ToDictionary(x => x.nom, x => x.valeur);
var dynamicValuesB = b.caracteristiques
.Where(x => _dynamicCriteria.Contains(x.nom))
.ToDictionary(x => x.nom, x => x.valeur);
// Compare dynamic criteria
foreach (var crit in _dynamicCriteria)
{
string valueA;
if (!dynamicValuesA.TryGetValue(crit, out valueA))
valueA = string.Empty;
string valueB;
if (!dynamicValuesB.TryGetValue(crit, out valueB))
valueB = string.Empty;
result = string.Compare(valueA, valueB);
if (result != 0)
return result;
}
// No difference in dynamic criteria, items are equal
return 0;
}
}
You can use this comparer like this:
var dynamicCriteria = new string[] { "largeur","longueur","epaisseur","type_pose" };
var sorted = listOfProducts
OrderBy(c => c, new MyComparer(dynamicCriteria));