Home > Software engineering >  Sort multiple list inside List of objects with the same order of the first list
Sort multiple list inside List of objects with the same order of the first list

Time:03-29

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 }
            },
        };
    }
}
  • Related