I Have this List of objects containing two lists of strings, that are linked I want to order the two lists valeurs
and ProductUid
depending on the order of valeurs.
listProducts:
[
{
"groupBy": "coloris",
"valeurs": [
"Beige",
"Gris clair",
"Anthracite",
"Beige",
"Augusta",
"Venezia"
],
"ProductUid": [
"TEST1",
"TEST2",
"TEST3",
"TEST4",
"TEST5",
"TEST6"
]
},
{
"groupBy": "ref_commercial",
"valeurs": [
"29245",
"51625",
"25269",
"29245",
"72585",
"72584"
],
"ProductUid": [
"TEST1",
"TEST2",
"TEST3",
"TEST4",
"TEST5",
"TEST6"
]
}
]
So the final result will be like this.
SortedResult:
[
{
"groupBy": "coloris",
"valeurs": [
"Anthracite",
"Augusta",
"Beige",
"Beige",
"Gris clair",
"Venezia"
],
"ProductUid": [
"TEST3",
"TEST5",
"TEST1",
"TEST4",
"TEST2",
"TEST6"
]
},
{
"groupBy": "ref_commercial",
"valeurs": [
"25269",
"29245",
"29245",
"51625",
"72584",
"72585"
],
"ProductUid": [
"TEST3",
"TEST1",
"TEST4",
"TEST2",
"TEST6"
"TEST5",
]
}
]
What I already did is
While creating the listProduct
var result = lProd.SelectMany(x => x.Caracteristiques.Distinct(), (parentObj, childnum) =>
new
{
parentObj,
childnum
})
.GroupBy(x => x.childnum.nom)
.Select(x => new
{
groupBy = x.Key,
valeurs = x.Select(z => z.childnum.valeur).OrderBy(q => q), // Sort List Valeurs
ProductUid = x.Select(z => z.parentObj.ProductUid), // Want to do the same sort here
}).Where(sid => OrdredList.Any(si => si == sid.groupBy))
.OrderBy(x => OrdredList.IndexOf(x.groupBy));
I was able to sort the valeurs
list but couldn't find a way to use the same sort on the productUID,
Is there a way to do it please ?
CodePudding user response:
You can create a list of indices and sort that by looking up values in the first list, and then use the indices to select values from the second. Assuming the lists are of equal length:
var l1 = new[] {1, 0, 2, 3};
var l2 = new[] {"b", "a", "c", "d"};
var indices = new int[l1.Length];
for (int i = 0; i < indices.Length; i )
{
indices[i] = i;
}
var sortedIndices = indices.OrderBy(i => l1[i]).ToList();
var l2Sorted = sortedIndices.Select(i => l2[i]).ToList();
var l1Sorted = sortedIndices.Select(i => l1[i]).ToList();
Console.WriteLine(string.Join(", ", l2Sorted));
CodePudding user response:
In order to make your task much easier I suggest you change the structure of the object to tuples of (property, uid). I would suggest doing this even if you didn't need to sort.
.Select(x => new
{
groupBy = x.Key,
valeursAndUids = x
.Select(z => (Valeur: z.childnum.valeur, Uid: z.parentObj.ProductUid))
.OrderBy(q => q.Valeur)
}
CodePudding user response:
This program produces the desired output:
var json = File.ReadAllText("data.json");
var list = JsonSerializer.Deserialize<List<X>>(json);
foreach(var x in list)
{
var combined = x.valeurs.Zip( x.ProductUid, (valuer, uid) => (valuer, uid) );
var sorted = combined.OrderBy(p => p.valuer);
x.valeurs = sorted.Select( p => p.valuer).ToList();
x.ProductUid = sorted.Select( p => p.uid).ToList();
// Or
// var combined = x.valeurs.Zip(x.ProductUid);
// var sorted = combined.OrderBy(p => p.First);
// x.valeurs = sorted.Select( p => p.First).ToList();
// x.ProductUid = sorted.Select( p => p.Second).ToList();
}
Console.WriteLine(JsonSerializer.Serialize(list, new JsonSerializerOptions()
{
WriteIndented = true
}));
class X {
public List<string> valeurs {get; set; }
public List<string> ProductUid {get; set; }
}
CodePudding user response:
Here's a relatively simple way to achieve this:
var x = j.Select(e => {
var map = e.Valeurs.Select((v, i) => (v, i)).OrderBy(t => t.v).Select(t => t.i).ToArray();
return new SomeNamespace.SomeRoot
{
GroupBy = e.GroupBy,
Valeurs = map.Select(i => e.Valeurs[i]).ToArray(),
ProductUid = map.Select(i => e.ProductUid[i]).ToArray(),
};
}
);
j
is an object representation of your original JSON. I'll give the classes that parse it at the end of the answer. In essence j
is a 2-long array of SomeRoot
, an object that has string GroupBy
, string[] Valuers
and string[] ProjectUids
properties. It represents the objects inside your root json array
The first thing we do, on this line:
var map = e.Valeurs.Select((v, i) => (v, i)).OrderBy(t => t.v).Select(t => t.i).ToArray();
is, for each root object, project the e.Valuers
to a tuple that includes the index at which the value is found at, i
. Then we sort by the value v
; this means we have a sorted array that remembers where the original element was found: Anthracite
was at index 2
but after sorting the array has a tuple of (Anthracite, 2)
as its first element. The whole array looks like:
[
(Anthracite, 2), <-- now in slot 0 but remembers it used to be in slot 2
(August, 4),
(Beige, 0),
(Beige, 3),
(Gris clair, 1)
(Venezia, 5)
]
This "memory of where it was" will be useful later. The next thing we do is throw the Anthracite
etc away and just keep the 2
etc
This means that map
is an array of ints that runs like [2,4,0,3,1,5]
If you access the original Valuers array in this order (2,4,0,3,1,5) you get the colors in order, Anthracite, August, Beige, Beige..
..and you can thus also use the same map array to access the ProjectUids to get those in that same order, 2,4,0,3,1,5
Hence your new arrays of Valuers and ProjectUids become:
Valeurs = map.Select(i => e.Valeurs[i]).ToArray(),
ProductUid = map.Select(i => e.ProductUid[i]).ToArray(),
You can think of it like *because map[0] == 2
then what was in index 2 in the original array is now in index 0 of the sorted array. The map array essentially maps "where it needs to be:where it actually is"
To change what you sort by, alter .OrderBy(t => t.v)
- t
is a tuple of v
, the original Valuer value, and i
, the index where it was found..
Here is the full code from your JSON to your requirement, including the classes for parsing your json:
//impl
var s =
@"[
{
""groupBy"": ""coloris"",
""valeurs"": [
""Beige"",
""Gris clair"",
""Anthracite"",
""Beige"",
""Augusta"",
""Venezia""
],
""ProductUid"": [
""TEST1"",
""TEST2"",
""TEST3"",
""TEST4"",
""TEST5"",
""TEST6""
]
},
{
""groupBy"": ""ref_commercial"",
""valeurs"": [
""29245"",
""51625"",
""25269"",
""29245"",
""72585"",
""72584""
],
""ProductUid"": [
""TEST1"",
""TEST2"",
""TEST3"",
""TEST4"",
""TEST5"",
""TEST6""
]
}
]";
var j = SomeNamespace.SomeRoot.FromJson(s);
var x = j.Select(e => {
var map = e.Valeurs.Select((v, i) => (v, i)).OrderBy(t => t.v).Select(t => t.i).ToArray();
return new SomeNamespace.SomeRoot
{
GroupBy = e.GroupBy,
Valeurs = map.Select(i => e.Valeurs[i]).ToArray(),
ProductUid = map.Select(i => e.ProductUid[i]).ToArray(),
};
}
);
//parsing classes
namespace SomeNamespace
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class SomeRoot
{
[JsonProperty("groupBy")]
public string GroupBy { get; set; }
[JsonProperty("valeurs")]
public string[] Valeurs { get; set; }
[JsonProperty("ProductUid")]
public string[] ProductUid { get; set; }
}
public partial class SomeRoot
{
public static SomeRoot[] FromJson(string json) => JsonConvert.DeserializeObject<SomeRoot[]>(json, SomeNamespace.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this SomeRoot[] self) => JsonConvert.SerializeObject(self, SomeNamespace.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
}