6 votes

Comment aplatir un tableau de tableaux ?

J'ai un tableau composé des éléments suivants :

var schools = new [] {
    new object[]{ new[]{ "1","2" }, "3","4" },
    new object[]{ new[]{ "5","6" }, "7","8" },
    new object[]{ new[]{ "9","10","11" }, "12","13" }
};

L'objet réel que j'essaie d'aplatir provient de l'importation de données dans un tableau de tableaux à partir d'un fichier CSV, puis d'une jointure sur les valeurs des champs :

    var q =
        from c in list
        join p in vocatives on c.Line[name1].ToUpper() equals p.first_name.ToUpper() into ps
        from p in ps.DefaultIfEmpty()
        select new object[] { c.Line,  p == null ? "(No vocative)" : p.vocative, p == null ? "(No sex)" : p.sex }; 

Je veux aplatir ce tableau de chaînes pour obtenir :

string[] {
    new string[]{ "1","2","3","4" },
    new string[]{ "5","6","7","8" },
    new string[]{ "9","10","11","12","13" }
}

J'ai déjà une solution qui fait cela en boucle, elle n'est pas très performante, mais elle semble fonctionner correctement.

J'ai essayé d'utiliser SelectMany mais ne peut pas trouver de solution.

Merci beaucoup pour vos commentaires ;) J'ai essayé la réponse de l'ONP :

var result = schools.Select(z => z.SelectMany(y=> y.GetType().IsArray 
           ? (object[])y : new object[] { y })
);

Mais la méthode de la classe CSVwriter n'accepte que les types explicites :

IEnumerable<string[]>

Alors comment le faire en linq, j'ai essayé :

List<string[]> listOflists = (List<string[]>)result;

Mais rien n'y fait, InvalidCastException se présente, malheureusement.

5voto

Oliver Points 19006

Dans un premier temps, vous devez normaliser les données en fonction d'un type de données. Ensuite, vous pouvez itérer sur les données comme vous le souhaitez. Créez donc d'abord une méthode pour aplatir les valeurs à partir d'un point spécifique jusqu'à une profondeur arbitraire :

public static class Extensions
{
    public static IEnumerable<object> FlattenArrays(this IEnumerable source)
    {
        foreach (var item in source)
        {
            if (item is IEnumerable inner
                && !(item is string))
            {
                foreach (var innerItem in inner.FlattenArrays())
                {
                    yield return innerItem;
                }
            }

            yield return item;
        }
    }
}

Vous pouvez maintenant itérer sur le niveau supérieur pour obtenir un tableau unique de toutes les valeurs :

// Produces one array => ["1", "2", "3", "4", ...]
var allFlat = schools.FlattenArrays().OfType<string>().ToArray();

Vous pouvez également créer des tableaux individuels plus profonds :

foreach (var item in schools)
{
    // Produces an array for each top level e.g. ["5", "6", "7", "8"]
    var flat = item.FlattenArrays().OfType<string>().ToArray();
}

2voto

StuartLC Points 35534

Comme indiqué dans les commentaires, parce que votre tableau interne mélange des éléments de string[] y string il ne sera probablement pas trivial de le faire directement en Linq.

Cependant, avec l'aide d'une fonction d'assistance (que j'ai appelée Flattener ), vous pouvez brancher manuellement le traitement des deux types internes pour renvoyer les éléments du tableau (s'il s'agit de string[] ), ou de renvoyer l'élément unique en tant qu'énumérable, si ce n'est pas le cas. SelectMany peut alors être utilisé pour aplatir le niveau intérieur, mais le niveau extérieur semble ne pas vouloir être aplati :

c'est-à-dire

var schools = new [] {
    new object[]{new[]{"1","2"}, "3","4"}, 
    new object[]{new[]{"5","6"}, "7","8"},
    new object[]{new[]{"9","10","11"}, "12","13"}
};

var result = schools
    .Select(s => s.SelectMany(o => Flattener(o)));

qui renvoie un type de IEnumerable<IEnumerable<string>>

Où le déballage en désordre est fait par :

public IEnumerable<string> Flattener(object o)
{
    if (o is IEnumerable<string> strings)
    {
        return strings;
    }
    if (o is string s)
    {
       return new[]{s};
    }
    return new[]{"?"};
}

Notez que ce qui précède utilise les capacités de filtrage de C#7.

Capture d'écran des résultats avec l'aimable autorisation de LinqPad :

enter image description here

0voto

npo Points 684

Si vous voulez le faire via linq, voici un exemple

     var schools = new[] {
    new object[]{new[]{"1","2"}, "3","4"},
    new object[]{new[]{"5","6"}, "7","8"},
    new object[]{new[]{"9","10","11"}, "12","13"}
};

var result = schools.Select(z => z.SelectMany(y=> y.GetType().IsArray ? (object[])y : new object[] { y }));

0voto

Ciro Corvino Points 1232

La solution présentée permet de convertir n'importe quelle sorte de int de la gamme, _régulière, irrégulière, ou imbriqué_ ( ces derniers sont tirés de javascript et de sa notation d'objets, mais ils peuvent également être mis en œuvre sous la forme d'un tableau complexe d'objets en C#. ), en un simple tableau d'entiers unidimensionnel.

Pour y adapter votre demande, il vous suffit de modifier le fichier string les éléments de votre tableau d'objets en dents de scie en int tipo.

Voici la fonction C# :

    public static int[] getFlattenedIntArray(object jaggedArray)
    {

        var flattenedArray = new List<int>();

        var jaggedArrayType = jaggedArray.GetType();
        var expectedType = typeof(int);

        if (jaggedArrayType.IsArray)
        {
            if (expectedType.IsAssignableFrom(jaggedArrayType.GetElementType()))
            {
                foreach (var el in jaggedArray as int[])
                {
                    flattenedArray.Add(el);
                }
            }
            else
            {
                foreach (var el in jaggedArray as object[])
                {
                    foreach (var retIntEl in getFlattenedIntArray(el))
                    {
                        flattenedArray.Add(retIntEl);
                    }
                }

            }
        }
        else if (jaggedArrayType == expectedType)
        {
            flattenedArray.Add((int)jaggedArray);
        }
        else
        {
            return new int[0];
        }

        return flattenedArray.ToArray();
    }

Essayez-le dans ce violon : https://dotnetfiddle.net/5HGX96

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X