ผสานสองอาร์เรย์ใน. NET


225

มีฟังก์ชันในตัวใน. NET 2.0 ที่จะใช้สองอาร์เรย์และรวมเข้าไปในอาร์เรย์เดียว

อาร์เรย์มีทั้งประเภทเดียวกัน ฉันได้รับอาร์เรย์เหล่านี้จากฟังก์ชั่นที่ใช้กันอย่างแพร่หลายภายในฐานรหัสของฉันและไม่สามารถแก้ไขฟังก์ชั่นเพื่อส่งคืนข้อมูลในรูปแบบที่แตกต่างกัน

ฉันกำลังมองหาที่จะหลีกเลี่ยงการเขียนฟังก์ชั่นของตัวเองเพื่อให้บรรลุนี้ถ้าเป็นไปได้

คำตอบ:


118

หากคุณสามารถจัดการกับหนึ่งในอาร์เรย์คุณสามารถปรับขนาดได้ก่อนดำเนินการคัดลอก:

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

มิฉะนั้นคุณสามารถสร้างอาร์เรย์ใหม่

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

เพิ่มเติมเกี่ยวกับวิธีการอาร์เรย์ที่มีอยู่บน MSDN


1
. NET 4.0 มีข่าวอะไรบ้าง?
Shimmy Weitzhandler

4
โปรดทราบArray.Resizeว่าไม่ได้ปรับขนาดอาร์เรย์จริง ๆ จะคัดลอกมัน นั่นเป็นเหตุผลที่พารามิเตอร์ตัวแรกคือ by-ref (ซึ่งหมายความว่ารหัสแรกของคุณอาจจะไม่รวบรวม)
CodesInChaos

2
ฉันเพิ่งจะโยนรหัสชิ้นแรกของคุณ มันไม่ได้มีข้อได้เปรียบและเป็นการยากที่จะอ่าน IMO
CodesInChaos

3
โปรดทราบว่าลำดับของพารามิเตอร์ในตัวอย่างรหัสที่สองสำหรับ Array.Copy นั้นผิด ใช้ Array.Copy (array1, newArray, 0); แทน.
marco birchler

คุณสามารถทำได้ .. List <byte> finalArray = new List <byte> (); finalArray.AddRange (array1); finalArray.AddRange (array2); ==> finalArray.toArray ();
Cédric Boivin

448

ใน C # 3.0 คุณสามารถใช้วิธีConcatของ LINQ เพื่อทำสิ่งนี้ได้อย่างง่ายดาย:

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

ใน C # 2.0 คุณไม่มีทางตรง แต่ Array.Copy น่าจะเป็นทางออกที่ดีที่สุด:

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

สิ่งนี้สามารถนำมาใช้เพื่อนำเวอร์ชันของConcatคุณไปใช้


1
ฉันชอบการนำ LINQ ไปใช้ ผมต้องการที่จะให้กระโดดและได้รับในเร็ว ๆ นี้ ... LINQ
GEOCHET

1
รวยส่วนที่ดีที่สุดเกี่ยวกับการใช้งาน LINQ ไม่เพียง แต่จะรัดกุม แต่ยังมีประสิทธิภาพเท่ากับรุ่น 2.0 เนื่องจากทำงานได้กับ IEnumerable
Brad Wilson

คำตอบนี้รวมถึงวิธีนี้และยังให้ผลลัพธ์การเปรียบเทียบ: stackoverflow.com/questions/415291/…
Demir

นี่อาจเป็นวิธีที่ง่ายที่สุด แต่สิ่งนี้จะไม่ได้ผลสำหรับอาร์เรย์ขนาดใหญ่เนื่องจาก Concat ถูกใช้งานโดยใช้ foreach loops + yield (ดูแหล่งอ้างอิง) การแก้ปัญหาด้วย BlockCopy จะเร็วขึ้น
tigrou

1
หัวเล็ก ๆ ขึ้นมา: ถ้าคุณต้องการวนซ้ำกับผลลัพธ์ที่รวมกันไม่จำเป็นต้องแปลงเป็นอาร์เรย์ การดำเนินการขั้นสุดท้ายนั้นทำการคัดลอกอาร์เรย์ มันไม่จำเป็นต้องทำอย่างนั้นถ้าคุณทำซ้ำผ่าน IEnumerable <int> แน่นอนอาจมีเหตุผลที่ดีที่จะมีอาร์เรย์
Jonas

82

ใช้LINQ :

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

โปรดทราบว่าสิ่งนี้จะลบรายการที่ซ้ำกัน หากคุณต้องการเก็บสำเนาซ้ำให้ใช้ Concat


132
ข้อควรระวัง: Union จะลบรายการที่ซ้ำกัน
Yogee

1
@ โยคีจดจำได้ง่ายเหมือนใน SQL และระบบการตั้งชื่อเชื่อมโยงกับทฤษฎีเซต
Zbigniew Wiadro

8
เนื่องจากมันจะลบรายการที่ซ้ำกันออกไปจึงไม่อาจเป็นคำตอบที่เหมาะสม
Roni Tovi

1
ฉันเห็นไซม่อนพูดถึงปัญหาของสหภาพแล้วและแนวทางอื่นที่เขาแนะนำ ไม่จำเป็นต้องพูดคุยเพิ่มเติมเกี่ยวกับสิ่งนั้นตามที่ Simon รู้ว่าเขาตอบอะไรอยู่
Sudhakar Chavali

41

หากคุณไม่ต้องการลบข้อมูลซ้ำให้ลองทำเช่นนี้

ใช้ LINQ:

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

11

ก่อนอื่นให้แน่ใจว่าคุณถามตัวเองด้วยคำถามว่า "ฉันควรใช้อาร์เรย์ที่นี่จริง ๆ " หรือไม่?

เว้นแต่ว่าคุณกำลังสร้างบางสิ่งที่ความเร็วมีความสำคัญสูงสุดรายการที่พิมพ์เช่นList<int>อาจเป็นหนทางไป ครั้งเดียวที่ฉันเคยใช้อาร์เรย์สำหรับไบต์อาร์เรย์เมื่อส่งข้อมูลผ่านเครือข่าย นอกจากนั้นฉันไม่เคยสัมผัสพวกเขา


ใหญ่ +1 ที่นี่ โปรดทราบว่าการปฏิบัติที่ดีที่สุดคือการหลีกเลี่ยงการเปิดเผยList<T>ใน APIs ส่วนกลาง: blogs.msdn.com/b/kcwalina/archive/2005/09/26/474010.aspx
TrueWill

10

ง่ายขึ้นเพียงใช้LINQ :

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

ก่อนอื่นให้แปลงอาร์เรย์เป็นรายการและทำการรวม ... หลังจากนั้นเพียงแปลงรายการกลับเป็นอาร์เรย์ :)


คุณไม่ได้ใช้อาร์เรย์โดยตรง คุณใช้รายการ!
Behzad Ebrahimi

7

ฉันคิดว่าคุณสามารถใช้Array.Copyได้ ใช้ดัชนีแหล่งที่มาและดัชนีปลายทางดังนั้นคุณควรจะสามารถผนวกหนึ่งอาร์เรย์ไปยังอีก หากคุณต้องการสลับซับซ้อนมากกว่าเพียงแค่ผนวกสิ่งต่อไปนี้อาจไม่ใช่เครื่องมือที่เหมาะสมสำหรับคุณ



4

โดยส่วนตัวแล้วฉันชอบส่วนขยายภาษาของตัวเองซึ่งฉันจะเพิ่มหรือลบตามความประสงค์เพื่อสร้างต้นแบบอย่างรวดเร็ว

ต่อไปนี้เป็นตัวอย่างสำหรับสตริง

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

มันเร็วกว่า LINQ และ Concat ยังเร็วกว่ากำลังใช้IEnumerableType-wrapper ที่กำหนดเองซึ่งเก็บการอ้างอิง / พอยน์เตอร์ของอาร์เรย์ที่ผ่านและอนุญาตให้วนรอบคอลเลกชันทั้งหมดราวกับว่ามันเป็นอาร์เรย์ปกติ (มีประโยชน์ใน HPC, การประมวลผลกราฟิก, การเรนเดอร์กราฟิก ... )

รหัสของคุณ:

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

สำหรับรหัสทั้งหมดและรุ่นทั่วไปโปรดดู: https://gist.github.com/lsauer/7919764

หมายเหตุ:นี่จะส่งคืนวัตถุ IEnumerable ที่ไม่สามารถนับได้ ในการส่งคืนออบเจ็กต์ส่วนขยายนั้นจะช้าลงเล็กน้อย

ฉันรวบรวมส่วนขยายดังกล่าวตั้งแต่ปี 2002 โดยมีเครดิตจำนวนมากจะช่วยผู้คนใน CodeProject และ 'Stackoverflow' ฉันจะปล่อยสิ่งเหล่านี้ในไม่ช้าและวางลิงก์ไว้ที่นี่


4

ทุกคนมีคำพูดของพวกเขาแล้ว แต่ฉันคิดว่าวิธีนี้สามารถอ่านได้ดีกว่าวิธี "ใช้เป็นวิธีการขยาย":

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

อย่างไรก็ตามสามารถใช้งานได้เมื่อรวบรวม 2 อาร์เรย์เท่านั้น


4

นี่คือสิ่งที่ฉันมาด้วย ใช้งานได้กับจำนวนอาร์เรย์ที่หลากหลาย

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

เพียงเพื่อให้มันบันทึกเป็นตัวเลือก: ถ้าอาร์เรย์ที่คุณกำลังทำงานอยู่นั้นเป็นประเภทดั้งเดิม - บูลีน (บูล), Char, SByte, ไบต์, Int16 (สั้น), UInt16, Int32 (int), UInt32, Int64 (ยาว) ) UInt64, IntPtr, UIntPtr เดี่ยวหรือเตียงคู่? - แล้วคุณสามารถ (หรือควร) ลองใช้Buffer.BlockCopy ตามหน้า MSDN สำหรับคลาสBuffer :

คลาสนี้ให้ประสิทธิภาพที่ดีขึ้นสำหรับการจัดการชนิดดั้งเดิมกว่าวิธีที่คล้ายกันในคลาสSystem.Array

การใช้ตัวอย่าง C # 2.0 จากคำตอบของ @ OwenP เป็นจุดเริ่มต้นมันจะทำงานดังนี้:

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

ไม่มีความแตกต่างในไวยากรณ์ระหว่างBuffer.BlockCopyและArray.Copy@OwenP ที่ใช้ แต่สิ่งนี้ควรจะเร็วกว่า (แม้ว่าจะเพียงเล็กน้อยเท่านั้น)


2

ในกรณีที่คนอื่นกำลังมองหาวิธีการรวมอาร์เรย์รูปภาพสองไบต์:

        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

นี่คือตัวอย่างง่ายๆโดยใช้ Array.CopyTo ฉันคิดว่ามันตอบคำถามของคุณและให้ตัวอย่างของการใช้ CopyTo - ฉันมักงงเมื่อฉันต้องใช้ฟังก์ชั่นนี้เพราะความช่วยเหลือนั้นค่อนข้างชัดเจน - ดัชนีเป็นตำแหน่งในอาเรย์ปลายทางที่มีการแทรกเกิดขึ้น

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

ฉันเดาว่าคุณคงไม่เข้าใจง่ายกว่านี้อีกแล้ว


1

ฉันต้องการวิธีแก้ไขเพื่อรวมจำนวนอาร์เรย์ที่ไม่รู้จัก

ไม่มีใครแปลกใจอื่น ๆ ที่จัดไว้ให้แก้ปัญหาโดยใช้กับSelectManyparams

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

หากคุณไม่ต้องการรายการที่แตกต่างเพียงแค่ลบที่แตกต่างกัน

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

หมายเหตุ: ไม่มีการรับประกันการสั่งซื้อแน่นอนเมื่อใช้งานที่แตกต่างกัน


0

ฉันสมมติว่าคุณใช้ประเภทอาเรย์ของคุณเองซึ่งต่างจากอาร์เรย์ในตัว. 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;
}

อีกวิธีหนึ่งในการทำเช่นนี้คือการใช้คลาสที่สร้างขึ้นใน 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;
}

ตัวอย่างทั้งสองคือ 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 + " ");  

การใช้โค้ดสองตัวข้างต้นสามารถรวมกันได้อย่างง่ายดาย


0

วิธีการสร้างและส่วนขยายเพื่อจัดการ 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

หากคุณมีอาร์เรย์ต้นทางในอาร์เรย์คุณสามารถใช้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);   

จะช่วยให้

1
2
3
4
5
6

อาจเป็นวิธีที่ไม่เร็วที่สุด แต่อาจเหมาะสมขึ้นอยู่กับ usecase


-1

รหัสนี้จะใช้ได้กับทุกกรณี:

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

คุณช่วยเพิ่มคำอธิบายเพิ่มเติมเกี่ยวกับโซลูชันที่คุณให้ได้ไหม
abarisone

1
ในขณะที่ข้อมูลโค้ดนี้อาจแก้ปัญหาได้รวมถึงคำอธิบายช่วยปรับปรุงคุณภาพของโพสต์ของคุณ จำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตและคนเหล่านั้นอาจไม่ทราบสาเหตุของการแนะนำรหัสของคุณ
gunr2171

สิ่งนี้ดูเหมือนจะเป็นการผสานที่เรียงลำดับซึ่งในขณะที่มีประโยชน์ในสิทธิของตนเอง (ส่วนใหญ่เป็นส่วนหนึ่งของกลยุทธ์การเรียกซ้ำ MergeSort) อาจมากกว่า OP ที่ขอ
Darrel Hoffman

ในขณะที่วิธีนี้ใช้งานได้การมีเทคนิคมากมายตั้งแต่ C # และ VB.Net ถูกนำมาใช้คนอาจไม่ชอบโซลูชันดังกล่าว
Sudhakar Chavali

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.