Fusionar dos matrices en .NET

225

¿Existe una función integrada en .NET 2.0 que tomará dos matrices y las fusionará en una sola?

Las matrices son del mismo tipo. Estoy obteniendo estas matrices de una función ampliamente utilizada dentro de mi base de código y no puedo modificar la función para devolver los datos en un formato diferente.

Estoy buscando evitar escribir mi propia función para lograr esto si es posible.

kbrinley
fuente

Respuestas:

118

Si puede manipular una de las matrices, puede cambiar su tamaño antes de realizar la copia:

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);

De lo contrario, puede hacer una nueva matriz

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);

Más información sobre los métodos de matriz disponibles en MSDN .

Blair Conrad
fuente
1
¿Qué pasa con .NET 4.0, alguna noticia?
Shimmy Weitzhandler
44
Tenga en cuenta que en Array.Resizerealidad no cambia el tamaño de la matriz, la copia. Es por eso que el primer parámetro es by-ref (lo que significa que su primer código probablemente no se compilará).
CodesInChaos
2
Acabo de tirar tu primer código. No ofrece una ventaja, y es más difícil de leer IMO.
CodesInChaos
3
Tenga en cuenta que el orden de los parámetros en el segundo ejemplo de código para Array.Copy es incorrecto. Use Array.Copy (array1, newArray, 0); en lugar.
marco birchler
También puede hacer .. List <byte> finalArray = new List <byte> (); finalArray.AddRange (array1); finalArray.AddRange (array2); ==> finalArray.toArray ();
Cédric Boivin
448

En C # 3.0 puede usar el método Concat de LINQ para lograr esto fácilmente:

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

En C # 2.0 no tiene una forma tan directa, pero Array.Copy es probablemente la mejor solución:

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);

Esto podría usarse fácilmente para implementar su propia versión de Concat.

OwenP
fuente
1
Me gusta esa implementación de LINQ. Realmente necesito dar el salto y entrar en LINQ pronto ...
GEOCHET
1
Rich, la mejor parte de la implementación de LINQ no solo es conciso, también es tan eficiente como la versión 2.0, ya que funciona contra IEnumerable.
Brad Wilson el
Esta respuesta incluye esta forma y también ofrece algunos resultados de evaluación comparativa: stackoverflow.com/questions/415291/…
Demir,
Esta es probablemente la forma más fácil, pero no será eficiente para matrices grandes, ya que Concat se implementa utilizando bucles foreach + rendimiento (ver fuente de referencia). Una solución con BlockCopy será más rápida.
tigrou
1
Solo un pequeño aviso: si solo desea recorrer el resultado combinado, no es necesario convertirlo en una matriz. Esa operación final hace una copia de matriz. No tendrá que hacer eso si itera a través de un IEnumerable <int>. Por supuesto, podría haber buenas razones para que sea una matriz.
Jonas
82

Use LINQ :

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

Tenga en cuenta que esto eliminará los duplicados. Si desea mantener duplicados, use Concat.

Simon B.
fuente
132
PRECAUCIÓN: Union eliminará los duplicados.
Yogee
1
@Yogee, fácil de recordar, ya que es como en SQL y también la nomenclatura está conectada con la teoría de conjuntos.
Zbigniew Wiadro
8
Como eliminará duplicados, nunca puede ser una respuesta adecuada.
Roni Tovi
1
Veo que Simon ya mencionó el tema de la Unión y el enfoque alternativo que sugirió. No hay necesidad de discutir más sobre eso, ya que Simon sabe lo que está respondiendo.
Sudhakar Chavali
41

Si no desea eliminar duplicados, intente esto

Use LINQ:

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

Primero, asegúrese de hacerse la pregunta "¿Realmente debería usar una matriz aquí"?

A menos que esté construyendo algo donde la velocidad sea de suma importancia, una lista con mecanografía, como, List<int>es probablemente el camino a seguir. La única vez que uso matrices es para matrices de bytes cuando envío cosas a través de la red. Aparte de eso, nunca los toco.

CodesInChaos
fuente
Gran +1 aquí. Tenga en cuenta que la mejor práctica es evitar exponerse List<T>en API públicas: blogs.msdn.com/b/kcwalina/archive/2005/09/26/474010.aspx
TrueWill
10

Más fácil sería usar LINQ :

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

Primero convierta los arreglos en listas y combínelos ... Después de eso, simplemente convierta la lista nuevamente en un arreglo :)

Angelo Ortega
fuente
No se utiliza matriz directamente. Usaste List!
Behzad Ebrahimi
7

Creo que puedes usar Array.Copy para esto. Toma un índice de origen y un índice de destino, por lo que debería poder agregar una matriz a la otra. Si necesita ser más complejo que simplemente agregar uno al otro, esta puede no ser la herramienta adecuada para usted.

GEOCHET
fuente
5

Asumiendo que la matriz de destino tiene suficiente espacio, Array.Copy()funcionará. También puede intentar usar ay List<T>su .AddRange()método.

Joel Coehoorn
fuente
4

Personalmente, prefiero mis propias extensiones de idioma, que agrego o elimino a voluntad para la creación rápida de prototipos.

El siguiente es un ejemplo de cadenas.

//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;
   }
}

Es mucho más rápido que LINQ y Concat. Aún más rápido, está utilizando un IEnumerableenvoltorio de tipo personalizado que almacena referencias / punteros de matrices pasadas y permite recorrer toda la colección como si fuera una matriz normal. (Útil en HPC, Procesamiento de gráficos, Procesamiento de gráficos ...)

Tu codigo:

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

Para ver el código completo y una versión genérica, consulte: https://gist.github.com/lsauer/7919764

Nota: Esto devuelve un objeto IEnumerable no extendido. Devolver un objeto extendido es un poco más lento.

Compilé tales extensiones desde 2002, con muchos créditos destinados a personas útiles en CodeProject y 'Stackoverflow'. Lo lanzaré en breve y pondré el enlace aquí.

Lorenz Lo Sauer
fuente
4

Todos ya han dado su opinión, pero creo que esto es más legible que el enfoque de "usar como método de extensión":

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

Sin embargo, solo se puede usar cuando se unen 2 matrices.

John Reilly
fuente
4

Esto es lo que se me ocurrió. Funciona para un número variable de matrices.

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
fuente
3

Solo para tenerlo como una opción: si las matrices con las que está trabajando son de un tipo primitivo: booleano (bool), Char, SByte, Byte, Int16 (corto), UInt16, Int32 (int), UInt32, Int64 (long ), UInt64, IntPtr, UIntPtr, Single o Double, entonces podría (o debería) intentar usar Buffer.BlockCopy . Según la página de MSDN para la clase Buffer :

Esta clase proporciona un mejor rendimiento para manipular tipos primitivos que métodos similares en la clase System.Array .

Usando el ejemplo C # 2.0 de la respuesta de @ OwenP como punto de partida, funcionaría de la siguiente manera:

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);

Apenas hay alguna diferencia en la sintaxis entre Buffer.BlockCopyy la Array.Copyque usó @OwenP, pero esto debería ser más rápido (aunque solo sea un poco).

Solomon Rutzky
fuente
2

En caso de que alguien más esté buscando cómo fusionar dos conjuntos de bytes de imagen:

        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
fuente
1

Aquí hay un ejemplo simple usando Array.CopyTo. Creo que responde a su pregunta y da un ejemplo del uso de CopyTo: siempre estoy confundido cuando necesito usar esta función porque la ayuda es un poco confusa: el índice es la posición en la matriz de destino donde se realiza la inserción.

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);

Supongo que no puedes hacerlo mucho más simple.

pasx
fuente
1

Necesitaba una solución para combinar un número desconocido de matrices.

Sorprendido, nadie más proporcionó una solución SelectManycon params.

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

Si no desea elementos distintos, simplemente elimine distintos.

 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);

Nota: Definitivamente no hay garantía de ordenar cuando se utiliza distinto.

Simon_Weaver
fuente
0

Supongo que está utilizando sus propios tipos de matriz en lugar de las matrices .NET integradas:

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;
}

Otra forma de hacerlo sería utilizar la clase integrada ArrayList.

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;
}

Ambos ejemplos son C #.

apandit
fuente
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 + " ");  

Usando el código anterior, dos matrices se pueden combinar fácilmente.

vikasse
fuente
0

Creado y método de extensión para manejar nulo

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
fuente
0

Si tiene las matrices de origen en una matriz en sí misma, puede usar 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);   

da

1
2
3
4
5
6

Probablemente este no sea el método más rápido, pero podría ajustarse dependiendo del caso de uso.

Schoetbi
fuente
-1

Este código funcionará para todos los casos:

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
fuente
¿Podría agregar más descripción sobre la solución que proporciona?
abarisone
1
Si bien este fragmento de código puede resolver la pregunta, incluir una explicación realmente ayuda a mejorar la calidad de su publicación. Recuerde que está respondiendo la pregunta para los lectores en el futuro, y que esas personas podrían no conocer los motivos de su sugerencia de código.
gunr2171
Esto parece ser una fusión ordenada, que si bien es útil por derecho propio (principalmente como parte de una estrategia recursiva MergeSort), podría ser más de lo que pedía el OP.
Darrel Hoffman el
Si bien esta solución funciona, teniendo muchas técnicas disponibles desde que se introdujo C # y VB.Net, las personas pueden no preferir tales soluciones.
Sudhakar Chavali
-2

Prueba esto:

ArrayLIst al = new ArrayList();
al.AddRange(array_1);
al.AddRange(array_2);
al.AddRange(array_3);
array_4 = al.ToArray();
namco
fuente