Fusion de deux tableaux dans .NET


225

Existe-t-il une fonction intégrée dans .NET 2.0 qui prendra deux tableaux et les fusionnera en un seul tableau?

Les tableaux sont tous les deux du même type. J'obtiens ces tableaux à partir d'une fonction largement utilisée dans ma base de code et je ne peux pas modifier la fonction pour renvoyer les données dans un format différent.

Je cherche à éviter d'écrire ma propre fonction pour y parvenir si possible.

Réponses:


118

Si vous pouvez manipuler l'un des tableaux, vous pouvez le redimensionner avant d'effectuer la copie:

T[] array1 = getOneArray();
T[] array2 = getAnotherArray();
int array1OriginalLength = array1.Length;
Array.Resize<T>(ref array1, array1OriginalLength + array2.Length);
Array.Copy(array2, 0, array1, array1OriginalLength, array2.Length);

Sinon, vous pouvez créer un nouveau tableau

T[] array1 = getOneArray();
T[] array2 = getAnotherArray();
T[] newArray = new T[array1.Length + array2.Length];
Array.Copy(array1, newArray, array1.Length);
Array.Copy(array2, 0, newArray, array1.Length, array2.Length);

Plus d'informations sur les méthodes Array disponibles sur MSDN .


1
Qu'en est-il de .NET 4.0, des nouvelles?
— Shimmy Weitzhandler

4
Notez que Array.Resizene redimensionne pas réellement le tableau, il le copie. C'est pourquoi le premier paramètre est by-ref (ce qui signifie que votre premier code ne sera probablement pas compilé).
— CodesInChaos

2
Je voudrais juste jeter votre premier morceau de code. Il n'offre aucun avantage et est plus difficile à lire IMO.
— CodesInChaos

3
Veuillez noter que l'ordre des paramètres dans le deuxième exemple de code pour Array.Copy est incorrect. Utilisez Array.Copy (array1, newArray, 0); au lieu.
— marco birchler

Vous pouvez également faire .. List <byte> finalArray = new List <byte> (); finalArray.AddRange (array1); finalArray.AddRange (array2); ==> finalArray.toArray ();
— Cédric Boivin

448

Dans C # 3.0, vous pouvez utiliser la méthode Concat de LINQ pour y parvenir facilement:

int[] front = { 1, 2, 3, 4 };
int[] back = { 5, 6, 7, 8 };
int[] combined = front.Concat(back).ToArray();

En C # 2.0, vous n'avez pas un moyen aussi direct, mais Array.Copy est probablement la meilleure solution:

int[] front = { 1, 2, 3, 4 };
int[] back = { 5, 6, 7, 8 };

int[] combined = new int[front.Length + back.Length];
Array.Copy(front, combined, front.Length);
Array.Copy(back, 0, combined, front.Length, back.Length);

Cela pourrait facilement être utilisé pour implémenter votre propre version de Concat.


1
J'aime cette implémentation LINQ. J'ai vraiment besoin de faire le saut et d'entrer dans LINQ bientôt ...
— GEOCHET

1
Riche, la meilleure partie de l'implémentation LINQ n'est pas seulement concise, elle est également aussi efficace que la version 2.0, car elle fonctionne contre IEnumerable.
— Brad Wilson, le

Cette réponse comprend cette façon et donne également des résultats d'analyse comparative: stackoverflow.com/questions/415291/…
— Demir

C'est probablement le moyen le plus simple mais cela ne sera pas efficace pour les grands tableaux, car Concat est implémenté en utilisant des boucles foreach + yield (voir la source de référence). Une solution avec BlockCopy sera plus rapide.
— tigrou

1
Juste un petit avertissement: si vous voulez seulement parcourir le résultat combiné, pas besoin de le convertir en tableau. Cette opération finale effectue une copie du tableau. Il n'aura pas à faire cela si vous parcourez un <int> IEnumerable. Bien sûr, il pourrait y avoir de bonnes raisons pour que ce soit un tableau.
— Jonas

82

Utilisez LINQ :

var arr1 = new[] { 1, 2, 3, 4, 5 };
var arr2 = new[] { 6, 7, 8, 9, 0 };
var arr = arr1.Union(arr2).ToArray();

Gardez à l'esprit que cela supprimera les doublons. Si vous souhaitez conserver les doublons, utilisez Concat.


132
ATTENTION: Union supprimera les doublons.
— Yogee

1
@Yogee, facile à retenir car c'est comme dans SQL et la nomenclature est également liée à la théorie des ensembles.
— Zbigniew Wiadro

8
car il supprimera les doublons, il ne pourra jamais être une bonne réponse.
— Roni Tovi

1
Je vois que Simon a déjà mentionné la question de l'Union et de l'approche alternative qu'il a suggérée. Pas besoin d'en discuter davantage car Simon sait à quoi il répond.
— Sudhakar Chavali

41

Si vous ne souhaitez pas supprimer les doublons, essayez ceci

Utilisez LINQ:

var arr1 = new[] { 1, 2, 3, 4, 5 };
var arr2 = new[] { 6, 7, 8, 9, 0 };
var arr = arr1.Concat(arr2).ToArray();

11

Tout d'abord, assurez-vous de vous poser la question "Dois-je vraiment utiliser un tableau ici"?

À moins que vous ne construisiez quelque chose où la vitesse est de la plus haute importance, une liste dactylographiée, comme List<int>c'est probablement le chemin à parcourir. La seule fois où j'utilise des tableaux est pour les tableaux d'octets lors de l'envoi de trucs sur le réseau. A part ça, je ne les touche jamais.


Big +1 ici. Notez que la meilleure pratique consiste à éviter l'exposition List<T>dans les API publiques: blogs.msdn.com/b/kcwalina/archive/2005/09/26/474010.aspx
— TrueWill

10

Plus simple serait simplement d'utiliser LINQ :

var array = new string[] { "test" }.ToList();
var array1 = new string[] { "test" }.ToList();
array.AddRange(array1);
var result = array.ToArray();

Convertissez d'abord les tableaux en listes et fusionnez-les ... Après cela, convertissez simplement la liste en tableau :)


Vous n'êtes pas directement utilisé tableau. Vous avez utilisé List!
— Behzad Ebrahimi

7

Je pense que vous pouvez utiliser Array.Copy pour cela. Il prend un index source et un index de destination, vous devriez donc pouvoir ajouter un tableau à l'autre. Si vous devez aller plus complexe que simplement ajouter l'un à l'autre, ce n'est peut-être pas le bon outil pour vous.



4

Personnellement, je préfère mes propres extensions linguistiques, que j'ajoute ou supprime à volonté pour un prototypage rapide.

Voici un exemple pour les chaînes.

//resides in IEnumerableStringExtensions.cs
public static class IEnumerableStringExtensions
{
   public static IEnumerable<string> Append(this string[] arrayInitial, string[] arrayToAppend)
   {
       string[] ret = new string[arrayInitial.Length + arrayToAppend.Length];
       arrayInitial.CopyTo(ret, 0);
       arrayToAppend.CopyTo(ret, arrayInitial.Length);

       return ret;
   }
}

C'est beaucoup plus rapide que LINQ et Concat. Plus rapide encore, utilise un IEnumerablewrapper de type personnalisé qui stocke les références / pointeurs des tableaux passés et permet de boucler sur toute la collection comme s'il s'agissait d'un tableau normal. (Utile en HPC, traitement graphique, rendu graphique ...)

Votre code:

var someStringArray = new[]{"a", "b", "c"};
var someStringArray2 = new[]{"d", "e", "f"};
someStringArray.Append(someStringArray2 ); //contains a,b,c,d,e,f

Pour le code complet et une version générique, voir: https://gist.github.com/lsauer/7919764

Remarque: Cela renvoie un objet IEnumerable non étendu. Retourner un objet étendu est un peu plus lent.

J'ai compilé de telles extensions depuis 2002, avec beaucoup de crédits destinés à des gens utiles sur CodeProject et 'Stackoverflow'. Je vais les publier sous peu et mettre le lien ici.


4

Tout le monde a déjà eu son mot à dire, mais je pense que cela est plus lisible que l'approche "utiliser comme méthode d'extension":

var arr1 = new[] { 1, 2, 3, 4, 5 };
var arr2 = new[] { 6, 7, 8, 9, 0 };
var arr = Queryable.Concat(arr1, arr2).ToArray();

Cependant, il ne peut être utilisé que lors du regroupement de 2 tableaux.


4

C'est ce que j'ai trouvé. Fonctionne pour un nombre variable de tableaux.

public static T[] ConcatArrays<T>(params T[][] args)
    {
        if (args == null)
            throw new ArgumentNullException();

        var offset = 0;
        var newLength = args.Sum(arr => arr.Length); 
        var newArray = new T[newLength];

        foreach (var arr in args)
        {
            Buffer.BlockCopy(arr, 0, newArray, offset, arr.Length);
            offset += arr.Length;
        }

        return newArray;
    }

...

var header = new byte[] { 0, 1, 2};
var data = new byte[] { 3, 4, 5, 6 };
var checksum = new byte[] {7, 0};
var newArray = ConcatArrays(header, data, checksum);
//output byte[9] { 0, 1, 2, 3, 4, 5, 6, 7, 0 }

3

Juste pour le noter en option: si les tableaux avec lesquels vous travaillez sont de type primitif - Boolean (bool), Char, SByte, Byte, Int16 (short), UInt16, Int32 (int), UInt32, Int64 (long ), UInt64, IntPtr, UIntPtr, Single ou Double - alors vous pourriez (ou devriez?) Essayer d'utiliser Buffer.BlockCopy . Selon la page MSDN de la classe Buffer :

Cette classe offre de meilleures performances pour la manipulation des types primitifs que les méthodes similaires de la classe System.Array .

En utilisant l'exemple C # 2.0 de la réponse de @ OwenP comme point de départ, cela fonctionnerait comme suit:

int[] front = { 1, 2, 3, 4 };
int[] back = { 5, 6, 7, 8 };

int[] combined = new int[front.Length + back.Length];
Buffer.BlockCopy(front, 0, combined, 0, front.Length);
Buffer.BlockCopy(back, 0, combined, front.Length, back.Length);

Il n'y a pratiquement pas de différence de syntaxe entre Buffer.BlockCopyet Array.Copycelle utilisée par @OwenP, mais cela devrait être plus rapide (même si ce n'est que légèrement).


2

Au cas où quelqu'un d'autre chercherait à fusionner deux tableaux d'octets d'image:

        private void LoadImage()
        {
            string src = string.empty;
            byte[] mergedImageData = new byte[0];

            mergedImageData = MergeTwoImageByteArrays(watermarkByteArray, backgroundImageByteArray);
            src = "data:image/png;base64," + Convert.ToBase64String(mergedImageData);
            MyImage.ImageUrl = src;
        }

        private byte[] MergeTwoImageByteArrays(byte[] imageBytes, byte[] imageBaseBytes)
        {
            byte[] mergedImageData = new byte[0];
            using (var msBase = new MemoryStream(imageBaseBytes))
            {
                System.Drawing.Image imgBase = System.Drawing.Image.FromStream(msBase);
                Graphics gBase = Graphics.FromImage(imgBase);
                using (var msInfo = new MemoryStream(imageBytes))
                {
                    System.Drawing.Image imgInfo = System.Drawing.Image.FromStream(msInfo);
                    Graphics gInfo = Graphics.FromImage(imgInfo);
                    gBase.DrawImage(imgInfo, new Point(0, 0));
                    //imgBase.Save(Server.MapPath("_____testImg.png"), ImageFormat.Png);
                    MemoryStream mergedImageStream = new MemoryStream();
                    imgBase.Save(mergedImageStream, ImageFormat.Png);
                    mergedImageData = mergedImageStream.ToArray();
                    mergedImageStream.Close();
                }
            }
            return mergedImageData;
        }

1

Voici un exemple simple utilisant Array.CopyTo. Je pense que cela répond à votre question et donne un exemple d'utilisation de CopyTo - Je suis toujours perplexe quand j'ai besoin d'utiliser cette fonction parce que l'aide est un peu floue - l'index est la position dans le tableau de destination où l'insertion a lieu.

int[] xSrc1 = new int[3] { 0, 1, 2 };
int[] xSrc2 = new int[5] { 3, 4, 5, 6 , 7 };

int[] xAll = new int[xSrc1.Length + xSrc2.Length];
xSrc1.CopyTo(xAll, 0);
xSrc2.CopyTo(xAll, xSrc1.Length);

Je suppose que vous ne pouvez pas obtenir beaucoup plus simple.


1

J'avais besoin d'une solution pour combiner un nombre inconnu de tableaux.

Surpris, personne d'autre n'a fourni de solution SelectManyavec params.

 private static T[] Combine<T>(params IEnumerable<T>[] items) =>
                    items.SelectMany(i => i).Distinct().ToArray();

Si vous ne voulez pas d'éléments distincts, supprimez simplement distinct.

 public string[] Reds = new [] { "Red", "Crimson", "TrafficLightRed" };
 public string[] Greens = new [] { "Green", "LimeGreen" };
 public string[] Blues = new [] { "Blue", "SkyBlue", "Navy" };

 public string[] Colors = Combine(Reds, Greens, Blues);

Remarque: Il n'y a certainement aucune garantie de commande lors de l'utilisation de distinct.


0

Je suppose que vous utilisez vos propres types de tableaux par opposition aux tableaux .NET intégrés:

public string[] merge(input1, input2)
{
    string[] output = new string[input1.length + input2.length];
    for(int i = 0; i < output.length; i++)
    {
        if (i >= input1.length)
            output[i] = input2[i-input1.length];
        else
            output[i] = input1[i];
    }
    return output;
}

Une autre façon de procéder serait d'utiliser la classe ArrayList intégrée.

public ArrayList merge(input1, input2)
{
    Arraylist output = new ArrayList();
    foreach(string val in input1)
        output.add(val);
    foreach(string val in input2)
        output.add(val);
    return output;
}

Les deux exemples sont C #.


0
int [] SouceArray1 = new int[] {2,1,3};
int [] SourceArray2 = new int[] {4,5,6};
int [] targetArray = new int [SouceArray1.Length + SourceArray2.Length];
SouceArray1.CopyTo(targetArray,0);
SourceArray2.CopyTo(targetArray,SouceArray1.Length) ; 
foreach (int i in targetArray) Console.WriteLine(i + " ");  

En utilisant le code ci-dessus, deux tableaux peuvent être facilement fusionnés.


0

Créé et méthode d'extension pour gérer null

public static class IEnumerableExtenions
{
    public static IEnumerable<T> UnionIfNotNull<T>(this IEnumerable<T> list1, IEnumerable<T> list2)
    {
        if (list1 != null && list2 != null)
            return list1.Union(list2);
        else if (list1 != null)
            return list1;
        else if (list2 != null)
            return list2;
        else return null;
    }
}

0

Si vous avez les tableaux source dans un tableau lui-même, vous pouvez utiliser SelectMany :

var arrays = new[]{new[]{1, 2, 3}, new[]{4, 5, 6}};
var combined = arrays.SelectMany(a => a).ToArray();
foreach (var v in combined) Console.WriteLine(v);   

donne

1
2
3
4
5
6

Ce n'est probablement pas la méthode la plus rapide mais pourrait convenir en fonction du cas d'utilisation.


-1

Ce code fonctionnera dans tous les cas:

int[] a1 ={3,4,5,6};
int[] a2 = {4,7,9};
int i = a1.Length-1;
int j = a2.Length-1;
int resultIndex=  i+j+1;
Array.Resize(ref a2, a1.Length +a2.Length);
while(resultIndex >=0)
{
    if(i != 0 && j !=0)
    {
        if(a1[i] > a2[j])
        {
            a2[resultIndex--] = a[i--];
        }
        else
        {
            a2[resultIndex--] = a[j--];
        }
    }
    else if(i>=0 && j<=0)
    { 
        a2[resultIndex--] = a[i--];
    }
    else if(j>=0 && i <=0)
    {
       a2[resultIndex--] = a[j--];
    }
}

Pourriez-vous s'il vous plaît ajouter plus de description sur la solution que vous fournissez?
— abarisone

1
Bien que cet extrait de code puisse résoudre la question, y compris une explication aide vraiment à améliorer la qualité de votre message. N'oubliez pas que vous répondrez à la question pour les lecteurs à l'avenir, et ces personnes pourraient ne pas connaître les raisons de votre suggestion de code.
— gunr2171

Cela semble être une fusion triée, qui, bien qu'utile en soi (principalement dans le cadre d'une stratégie récursive MergeSort), pourrait être plus que ce que le PO demandait.
— Darrel Hoffman le

Bien que cette solution fonctionne, le fait d'avoir beaucoup de techniques disponibles depuis l'introduction de C # et VB.Net peut ne pas préférer de telles solutions.
— Sudhakar Chavali

-2

Essaye ça:

ArrayLIst al = new ArrayList();
al.AddRange(array_1);
al.AddRange(array_2);
al.AddRange(array_3);
array_4 = al.ToArray();
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.