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.

kbrinley
la source

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 .

Blair Conrad
la source
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.

OwenP
la source
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.

Simon B.
la source
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();
Forgeron
la source
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.

CodesInChaos
la source
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 :)

Angelo Ortega
la source
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.

GEOCHET
la source
5

En supposant que le tableau de destination dispose de suffisamment d'espace, Array.Copy()cela fonctionnera. Vous pouvez également essayer d'utiliser a List<T>et sa .AddRange()méthode.

Joel Coehoorn
la source
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.

Lorenz Lo Sauer
la source
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.

John Reilly
la source
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 }
cj.burrow
la source
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).

Solomon Rutzky
la source
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;
        }
Lukas
la source
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.

pasx
la source
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.

Simon_Weaver
la source
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 #.

apandit
la source
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.

vikasse
la source
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;
    }
}
Lord Darth Vader
la source
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.

schoetbi
la source
-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--];
    }
}
Rajkumar M
la source
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();
namco
la source