Scalenie dwóch tablic w .NET

225

Czy w .NET 2.0 jest wbudowana funkcja, która pobierze dwie tablice i połączy je w jedną tablicę?

Obie tablice są tego samego typu. Otrzymuję te tablice z powszechnie używanej funkcji w mojej bazie kodu i nie mogę modyfikować funkcji, aby zwracać dane w innym formacie.

Staram się unikać pisania własnej funkcji, aby to osiągnąć, jeśli to możliwe.

Kbrinley
źródło

Odpowiedzi:

118

Jeśli możesz manipulować jedną z tablic, możesz zmienić jej rozmiar przed wykonaniem kopii:

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

W przeciwnym razie możesz utworzyć nową tablicę

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

Więcej informacji o dostępnych metodach Array w MSDN .

Blair Conrad
źródło
1
Co z .NET 4.0, jakieś nowości?
Shimmy Weitzhandler,
4
Zauważ, że Array.Resizetak naprawdę nie zmienia rozmiaru tablicy, tylko ją kopiuje. Dlatego pierwszym parametrem jest by-ref (co oznacza, że ​​twój pierwszy kod prawdopodobnie się nie skompiluje).
CodesInChaos
2
Po prostu wyrzucę twój pierwszy fragment kodu. Nie daje przewagi i jest trudniejszy do odczytania przez IMO.
CodesInChaos
3
Należy pamiętać, że kolejność parametrów w drugim przykładzie kodu dla Array.Copy jest niepoprawna. Użyj Array.Copy (array1, newArray, 0); zamiast.
marco birchler
Możesz także zrobić .. List <byte> finalArray = new List <byte> (); finalArray.AddRange (array1); finalArray.AddRange (array2); ==> finalArray.toArray ();
Cédric Boivin
448

W C # 3.0 możesz użyć metody Concat LINQ, aby łatwo to osiągnąć:

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

W C # 2.0 nie masz takiego bezpośredniego sposobu, ale Array.Copy jest prawdopodobnie najlepszym rozwiązaniem:

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

Można to łatwo wykorzystać do wdrożenia własnej wersji Concat.

OwenP
źródło
1
Podoba mi się ta implementacja LINQ. Naprawdę muszę skoczyć i wkrótce dostać się do LINQ ...
GEOCHET
1
Bogaty, najlepsza część dotycząca implementacji LINQ jest nie tylko zwięzła, ale także tak samo wydajna jak wersja 2.0, ponieważ działa przeciwko IEnumerable.
Brad Wilson,
Ta odpowiedź obejmuje ten sposób, a także podaje wyniki analizy porównawczej: stackoverflow.com/questions/415291/…
Demir,
Jest to prawdopodobnie najłatwiejszy sposób, ale nie będzie to efektywne w przypadku dużych tablic, ponieważ Concat jest implementowany przy użyciu pętli foreach + wydajności (patrz źródło odniesienia). Rozwiązanie z BlockCopy będzie szybsze.
tigrou
1
Tylko mała uwaga: jeśli chcesz tylko iterować połączony wynik, nie musisz konwertować go na tablicę. Ta ostatnia operacja wykonuje kopię tablicy. Nie będzie musiał tego robić, jeśli wykonasz iterację przez IEnumerable <int>. Oczywiście mogą istnieć dobre powody, aby mieć tablicę.
Jonas,
82

Użyj LINQ :

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

Pamiętaj, że spowoduje to usunięcie duplikatów. Jeśli chcesz zachować duplikaty, użyj Concat.

Simon B.
źródło
132
UWAGA: Unia usunie duplikaty.
Yogee
1
@Yogee, łatwe do zapamiętania, tak jak w SQL, a także nomenklatura związana jest z teorią zbiorów.
Zbigniew Wiadro
8
ponieważ usunie duplikaty, nigdy nie może być poprawną odpowiedzią.
Roni Tovi
1
Widzę, że Simon wspomniał już o kwestii Unii i alternatywnego podejścia, które zasugerował. Nie trzeba więcej o tym rozmawiać, ponieważ Simon wie, na co odpowiada.
Sudhakar Chavali,
41

Jeśli nie chcesz usuwać duplikatów, spróbuj tego

Użyj LINQ:

var arr1 = new[] { 1, 2, 3, 4, 5 };
var arr2 = new[] { 6, 7, 8, 9, 0 };
var arr = arr1.Concat(arr2).ToArray();
Kowal
źródło
11

Po pierwsze, zadaj sobie pytanie „Czy naprawdę powinienem tutaj używać tablicy”?

O ile nie budujesz czegoś, w którym szybkość ma ogromne znaczenie, lista na maszynie List<int>jest podobna . Jedynym razem, gdy korzystam z tablic, są tablice bajtowe podczas wysyłania danych przez sieć. Poza tym nigdy ich nie dotykam.

CodesInChaos
źródło
Duże +1 tutaj. Należy pamiętać, że najlepszą praktyką jest unikanie ujawniania List<T>w publicznych interfejsach API: blogs.msdn.com/b/kcwalina/archive/2005/09/26/474010.aspx
TrueWill
10

Łatwiej byłoby po prostu użyć LINQ :

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

Najpierw przekonwertuj tablice na listy i scal je ... Następnie po prostu przekonwertuj listę z powrotem na tablicę :)

Angelo Ortega
źródło
Nie używasz bezpośrednio tablicy. Użyłeś listy!
Behzad Ebrahimi,
7

Myślę, że możesz do tego użyć Array.Copy . Pobiera indeks źródłowy i docelowy, więc powinieneś być w stanie dołączyć jedną tablicę do drugiej. Jeśli potrzebujesz bardziej skomplikowanego niż tylko dołączanie jednego do drugiego, może to nie być odpowiednie narzędzie dla Ciebie.

GEOCHET
źródło
5

Zakładając, że tablica docelowa ma wystarczającą ilość miejsca, Array.Copy()będzie działać. Możesz także spróbować użyć metody a List<T>i jej .AddRange()metody.

Joel Coehoorn
źródło
4

Osobiście wolę własne rozszerzenia językowe, które dodam lub usunę do woli w celu szybkiego prototypowania.

Poniżej znajduje się przykład ciągów.

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

Jest znacznie szybszy niż LINQ i Concat. Jeszcze szybciej korzysta z niestandardowego IEnumerableopakowania typu, które przechowuje referencje / wskaźniki przekazanych tablic i umożliwia zapętlanie całej kolekcji, jak gdyby była to normalna tablica. (Przydatne w HPC, przetwarzaniu grafiki, renderowaniu grafiki ...)

Twój kod:

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

Cały kod i ogólna wersja patrz: https://gist.github.com/lsauer/7919764

Uwaga: Zwraca nierozszerzony obiekt IEnumerable. Zwrócenie rozszerzonego obiektu jest nieco wolniejsze.

Kompilowałem takie rozszerzenia od 2002 roku, a wiele kredytów trafiło do pomocnych osób na CodeProject i „Stackoverflow”. Niedługo je wypuszczę i zamieszczę link tutaj.

Lorenz Lo Sauer
źródło
4

Wszyscy mają już swoje zdanie, ale myślę, że jest to bardziej czytelne niż podejście „użyj jako metody rozszerzenia”:

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

Można go jednak użyć tylko w przypadku połączenia 2 tablic.

John Reilly
źródło
4

Właśnie to wymyśliłem. Działa dla zmiennej liczby tablic.

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
źródło
3

Wystarczy zaznaczyć to jako opcję: jeśli macierze, z którymi pracujesz, są typu pierwotnego - Boolean (bool), Char, SByte, Byte, Int16 (krótki), UInt16, Int32 (int), UInt32, Int64 (długi ), UInt64, IntPtr, UIntPtr, Single lub Double - wtedy możesz (lub powinieneś?) Spróbować użyć Buffer.BlockCopy . Zgodnie ze stroną MSDN dla klasy Buffer :

Ta klasa zapewnia lepszą wydajność manipulowania typami pierwotnymi niż podobne metody w klasie System.Array .

Korzystanie z C # 2.0 przykład z @ OwenP za odpowiedź za punkt wyjścia, że to działa w następujący sposób:

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

Nie ma prawie żadnej różnicy w składni Buffer.BlockCopyi Array.Copyużytej @OwenP, ale powinno to być szybsze (nawet jeśli tylko nieznacznie).

Solomon Rutzky
źródło
2

W przypadku, gdy ktoś inny szuka sposobu połączenia dwóch tablic bajtów obrazkowych:

        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
źródło
1

Oto prosty przykład użycia Array.CopyTo. Myślę, że odpowiada na twoje pytanie i podaje przykład użycia CopyTo - zawsze jestem zdziwiony, kiedy muszę użyć tej funkcji, ponieważ pomoc jest nieco niejasna - indeks jest pozycją w tablicy docelowej, w której występuje wstawianie.

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

Myślę, że nie można tego uprościć.

pasx
źródło
1

Potrzebowałem rozwiązania, aby połączyć nieznaną liczbę tablic.

Zaskoczony nikt inny nie dostarcza rozwiązania z wykorzystaniem SelectManyz params.

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

Jeśli nie chcesz odrębnych elementów, po prostu usuń wyraźne.

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

Uwaga: Zdecydowanie nie ma gwarancji zamówienia przy użyciu odrębnego.

Simon_Weaver
źródło
0

Zakładam, że używasz własnych typów tablic w przeciwieństwie do wbudowanych tablic .NET:

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

Innym sposobem na to byłoby użycie wbudowanej klasy 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;
}

Oba przykłady to C #.

apandit
źródło
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 + " ");  

Za pomocą powyższego kodu można łatwo połączyć dwie tablice.

Vikasse
źródło
0

Utworzono metodę rozszerzenia do obsługi wartości 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
źródło
0

Jeśli masz tablice źródłowe w samej tablicy, możesz użyć 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);   

daje

1
2
3
4
5
6

Prawdopodobnie nie jest to najszybsza metoda, ale może pasować w zależności od przypadku użycia.

schoetbi
źródło
-1

Ten kod będzie działał we wszystkich przypadkach:

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.
źródło
Czy możesz dodać więcej informacji na temat oferowanego rozwiązania?
abarisone
1
Ten fragment kodu może rozwiązać pytanie, ale wyjaśnienie naprawdę pomaga poprawić jakość posta. Pamiętaj, że w przyszłości odpowiadasz na pytanie dla czytelników, a ci ludzie mogą nie znać przyczyn Twojej sugestii kodu.
gunr2171
Wydaje się, że jest to posortowane scalenie, które chociaż jest przydatne samo w sobie (głównie w ramach strategii rekurencyjnej MergeSort), może być czymś więcej niż wymagało PO.
Darrel Hoffman
Chociaż to rozwiązanie działa, mając wiele technik dostępnych od czasu wprowadzenia C # i VB.Net, ludzie mogą nie preferować takich rozwiązań.
Sudhakar Chavali,
-2

Spróbuj tego:

ArrayLIst al = new ArrayList();
al.AddRange(array_1);
al.AddRange(array_2);
al.AddRange(array_3);
array_4 = al.ToArray();
namco
źródło