C # में ऐरे स्लाइस


228

आप इसे कैसे करते हो? एक बाइट सरणी को देखते हुए:

byte[] foo = new byte[4096];

मुझे सरणी के पहले x बाइट्स को एक अलग सरणी के रूप में कैसे मिलेगा? (विशेष रूप से, मुझे इसकी आवश्यकता है IEnumerable<byte>)

यह Sockets के साथ काम करने के लिए है । मुझे लगता है कि सबसे आसान तरीका यह होगा कि स्लाइसिंग, पर्ल्स सिंटैक्स के समान हो:

@bar = @foo[0..40];

जो @barसरणी में पहले 41 तत्वों को लौटाएगा । क्या C # में कुछ ऐसा है जो मुझे बस याद आ रहा है, या कोई और चीज है जो मुझे करनी चाहिए?

LINQ मेरे लिए एक विकल्प है (.NET 3.5), अगर इससे कोई मदद मिलती है।


3
ऐरे स्लाइसिंग c # 7.2 github.com/dotnet/csharplang/issues/185 के
मार्क

3
C # 8.0 देशी सरणी टुकड़ा करने की क्रिया को देखेगा। अधिक विवरण के लिए उत्तर देखें
रेमी

1
आपको ArraySlice में रुचि हो सकती है <T> जो मूल डेटा पर एक दृश्य के रूप में कदम के साथ सरणियों के स्लाइसिंग को लागू करता है: github.com/henon/SliceAndDice
henon

जवाबों:


196

Arrays enumerable हैं, इसलिए आपका fooपहले से IEnumerable<byte>ही एक है। बस LINQ अनुक्रम विधियों का उपयोग करें जैसे Take()कि आप इससे क्या चाहते हैं ( Linqनाम स्थान को शामिल करना न भूलें using System.Linq;):

byte[] foo = new byte[4096];

var bar = foo.Take(41);

यदि आपको वास्तव में किसी भी IEnumerable<byte>मूल्य से एक सरणी की आवश्यकता है , तो आप उसके लिए ToArray()विधि का उपयोग कर सकते हैं । यहां ऐसा नहीं लगता है।


5
यदि हम किसी अन्य सरणी में कॉपी करने जा रहे हैं तो Array.Copy स्टेटिक विधि का उपयोग करें। हालांकि मुझे लगता है कि अन्य उत्तरों ने इरादे को सही ढंग से व्याख्या की है, पहले 41 बाइट्स पर केवल एक IEnumberable <बाइट> एक और सरणी की आवश्यकता नहीं है।
एंथनीवजोन 11

2
ध्यान दें कि केवल एकल आयामी और दांतेदार सरणियाँ असंख्य हैं, बहुआयामी सरणियाँ नहीं हैं।
हाबिल

11
Array.Copy का उपयोग करने पर ध्यान दें LINQ के टेक या स्किप के तरीकों का उपयोग करने की तुलना में बहुत तेज प्रदर्शन करता है।
माइकल

4
@ अविश्वास वास्तव में बहुत गलत है। बहु आयामी सरणी हैं गणनीय लेकिन वे इस तरह की गणना: [2,3] => [1,1], [1,2], [1,3], [2,1], [2,2], [2,3]। दांतेदार सरणियों भी enumerable हैं, लेकिन जब enumerated एक मान लौटने के बजाय, वे अपने आंतरिक सरणी लौटाते हैं। इस तरह:type[][] jaggedArray; foreach (type[] innerArray in jaggedArray) { }
Aidiakapi

3
@ अदीकापी "बहुत ही अनिश्चित"? ;)। लेकिन आप आंशिक रूप से सही हैं, मुझे "मल्टीडिम सरणियों को लागू नहीं करना" लिखना चाहिए IEnumerable<T>, फिर मेरा कथन स्पष्ट हो गया। इसे भी देखें: stackoverflow.com/questions/721882/…
Abel

211

आप उपयोग कर सकते हैं ArraySegment<T>। यह बहुत हल्का है क्योंकि यह सरणी की नकल नहीं करता है:

string[] a = { "one", "two", "three", "four", "five" };
var segment = new ArraySegment<string>( a, 1, 2 );

5
दुर्भाग्य से यह IEnumerable नहीं है।
पुनरावर्ती

1
सच है, लेकिन इसके चारों ओर एक इटरेटर आवरण लिखना आसान होगा जो IEnumerable को लागू करता है।
माइक स्कॉट १

22
क्या किसी को पता है कि यह IEnumerable क्यों नहीं है? मैं नही। ऐसा लगता है जैसे यह होना चाहिए।
फंक्शियस डिस

39
ArraySegment IList और IEnumerable .Net 4.5 से शुरू होता है। पुराने संस्करण उपयोगकर्ताओं के लिए बहुत बुरा है ..
टोड ली

6
@Zyo का मतलब था कि ArraySegment <T> औजार IEnumerable <T> .Net 4.5 से शुरू होकर, IEnumerable <T> अपने आप में नया नहीं है।
टॉड ली

137

आप ऐरे CopyTo()विधि का उपयोग कर सकते हैं ।

या LINQ के साथ आप उपयोग कर सकते हैं Skip()और Take()...

byte[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
var subset = arr.Skip(2).Take(2);

1
एक अच्छे विचार के लिए +1, लेकिन मुझे दूसरे फ़ंक्शन के इनपुट के रूप में दिए गए एरे का उपयोग करने की आवश्यकता है, जिससे कॉपीटो को एक अस्थायी चर की आवश्यकता होती है। मैं अन्य उत्तरों की प्रतीक्षा करूंगा।
मैथ्यू शार्ले

4
मैं LINQ से अभी तक परिचित नहीं हूँ, शायद यह आगे का सबूत है कि मुझे वास्तव में होना चाहिए।
मैथ्यू शेर्ले

11
यह दृष्टिकोण Array.Copy की तुलना में कम से कम 50x धीमा है। यह कई स्थितियों में एक समस्या नहीं है, लेकिन जब एक चक्र में सरणी टुकड़ा करना होता है, तो प्रदर्शन ड्रॉप बहुत स्पष्ट होता है।
वैलेंटाइन वसीलीव

3
मैं सिंगल कॉल कर रहा हूं, इसलिए प्रदर्शन मेरे लिए कोई समस्या नहीं है। यह पठनीयता के लिए बहुत अच्छा है ... धन्यवाद।
रिच

2
के लिए धन्यवाद Skip()। बस Take()आपको एक मनमाना टुकड़ा नहीं मिलेगा। इसके अलावा, मैं वैसे भी एक LINQ समाधान की तलाश कर रहा था (टुकड़ा IEnumerable, लेकिन मुझे पता था कि सरणी के बारे में परिणाम खोजना आसान होगा)।
टॉमस गैंडर

55
static byte[] SliceMe(byte[] source, int length)
{
    byte[] destfoo = new byte[length];
    Array.Copy(source, 0, destfoo, 0, length);
    return destfoo;
}

//

var myslice = SliceMe(sourcearray,41);

11
मुझे लगता है कि Buffer.BlockCopy () अधिक कुशल है और समान परिणाम प्राप्त करता है।
मैट डेविस

28

C # 8.0 / .net Core 3.0 से शुरू

नए प्रकार के साथ Indexऔर Rangeजोड़ा जा रहा है , ऐरे स्लाइसिंग का समर्थन किया जाएगा ।

रेंज स्ट्रक्चर डॉक्स
इंडेक्स स्ट्रक्चर डॉक्स

Index i1 = 3;  // number 3 from beginning
Index i2 = ^4; // number 4 from end
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"

var slice = a[i1..i2]; // { 3, 4, 5 }

ऊपर कोड नमूना C # 8.0 ब्लॉग से लिया गया है ।

ध्यान दें कि ^उपसर्ग सरणी के अंत से गिनती को इंगित करता है । जैसा कि डॉक्स उदाहरण में दिखाया गया है

var words = new string[]
{
                // index from start    index from end
    "The",      // 0                   ^9
    "quick",    // 1                   ^8
    "brown",    // 2                   ^7
    "fox",      // 3                   ^6
    "jumped",   // 4                   ^5
    "over",     // 5                   ^4
    "the",      // 6                   ^3
    "lazy",     // 7                   ^2
    "dog"       // 8                   ^1
};              // 9 (or words.Length) ^0

Rangeऔर Indexस्लोपिंग सरणियों के बाहर भी काम करते हैं, उदाहरण के लिए छोरों के साथ

Range range = 1..4; 
foreach (var name in names[range])

प्रविष्टियों 1 के माध्यम से 4 के माध्यम से लूप जाएगा


ध्यान दें कि इस उत्तर को लिखते समय, C # 8.0 अभी तक आधिकारिक रूप से जारी नहीं किया गया है
C # 8.x और .Net Core 3.x है जो अब विजुअल स्टूडियो 2019 और उसके बाद उपलब्ध है।


किसी भी विचार या नहीं यह सरणी की एक प्रति बनाता है?
टिम पोहलमैन

2
ऐसा लगता है कि यह एक प्रतिलिपि है: codejourney.net/2019/02/csharp-8-slicing-indexes-ranges
टिम

22

में सी # 7.2 , तो आप उपयोग कर सकते हैं Span<T>। नए का लाभSystem.Memory प्रणाली का यह है कि इसे डेटा के आसपास कॉपी करने की आवश्यकता नहीं है।

आपके लिए आवश्यक विधि है Slice:

Span<byte> slice = foo.Slice(0, 40);

बहुत सारे तरीके अब समर्थन करते हैं Spanऔर IReadOnlySpanइसलिए, इस नए प्रकार का उपयोग करना बहुत सरल होगा।

ध्यान दें कि लिखने के समय Span<T>प्रकार .NET के सबसे हाल के संस्करण में अभी तक परिभाषित नहीं किया गया है (4.7.1) इसलिए इसका उपयोग करने के लिए आपको System.Memory पैकेज को NuGet से इंस्टॉल करने की आवश्यकता है ।


1
ध्यान दें कि Span<T>प्रकार .Net के सबसे हाल के संस्करण में अभी तक परिभाषित नहीं किया गया है (4.7.1) इसलिए इसका उपयोग करने के लिए आपको System.MemoryNuGet से इंस्टॉल करने की आवश्यकता है (और याद रखें कि NuGet में खोज करते समय "प्रीलेइज़ को शामिल करें")
मैथ्यू वाटसन

@MatthewWatson धन्यवाद मैंने आपकी टिप्पणी पुनः लिखी और अपने उत्तर में जोड़ दी।
पैट्रिक हॉफमैन

16

एक अन्य संभावना जिसका मैंने यहां उल्लेख नहीं किया है: बफ़र.ब्लॉककॉपी () एरे.कोपी () की तुलना में थोड़ा तेज़ है, और इसमें प्राइमेटिव के एक सरणी से ऑन-द-फ्लाई में परिवर्तित होने में सक्षम होने का अतिरिक्त लाभ है (कहते हैं, लघु []] बाइट्स की एक सरणी के लिए, जो तब आपके काम के लिए सांकेतिक सरणियाँ प्राप्त कर सकता है जो आपको सॉकेट्स पर प्रसारित करने की आवश्यकता होती है।


2
Buffer.BlockCopyअलग-अलग परिणामों का उत्पादन Array.Copy()करने के बावजूद वे एक ही पैरामीटर को स्वीकार करते हैं - बहुत सारे खाली तत्व थे। क्यों?
jocull

7
@ जोकल - वे वास्तव में एक ही पैरामीटर नहीं लेते हैं। Array.Copy () तत्वों में इसकी लंबाई और स्थिति पैरामीटर लेता है। बफ़र.ब्लॉककॉपी () बाइट्स में अपनी लंबाई और स्थिति पैरामीटर लेता है। दूसरे शब्दों में, यदि आप पूर्णांक के 10-तत्व सरणी की प्रतिलिपि बनाना चाहते हैं, तो आप उपयोग करेंगे Array.Copy(array1, 0, array2, 0, 10), लेकिन Buffer.BlockCopy(array1, 0, array2, 0, 10 * sizeof(int))
केन स्मिथ


14

यहाँ एक सरल विस्तार विधि है जो एक नए सरणी के रूप में एक टुकड़ा देता है:

public static T[] Slice<T>(this T[] arr, uint indexFrom, uint indexTo) {
    if (indexFrom > indexTo) {
        throw new ArgumentOutOfRangeException("indexFrom is bigger than indexTo!");
    }

    uint length = indexTo - indexFrom;
    T[] result = new T[length];
    Array.Copy(arr, indexFrom, result, 0, length);

    return result;
}

तब आप इसका उपयोग कर सकते हैं:

byte[] slice = foo.Slice(0, 40);

8

यदि आप LINQ या अन्य एक्सटेंशन नहीं जोड़ना चाहते हैं , तो:

float[] subArray = new List<float>(myArray).GetRange(0, 8).ToArray();

Error CS0246: The type or namespace name 'List<>' could not be found (are you missing a using directive or an assembly reference?) Microsoft दस्तावेज़ीकरण सैकड़ों "सूची" प्रविष्टियों को अनुक्रमित करने के साथ निराशाजनक है। यहाँ क्या सही है?
wallyk

1
System.Collections.Generic.List
टेट्रालक्स

7

आप मूल सरणी के चारों ओर एक आवरण का उपयोग कर सकते हैं (जो कि IList है), जैसे कि यह (अनुपचारित) कोड का टुकड़ा।

public class SubList<T> : IList<T>
{
    #region Fields

private readonly int startIndex;
private readonly int endIndex;
private readonly int count;
private readonly IList<T> source;

#endregion

public SubList(IList<T> source, int startIndex, int count)
{
    this.source = source;
    this.startIndex = startIndex;
    this.count = count;
    this.endIndex = this.startIndex + this.count - 1;
}

#region IList<T> Members

public int IndexOf(T item)
{
    if (item != null)
    {
        for (int i = this.startIndex; i <= this.endIndex; i++)
        {
            if (item.Equals(this.source[i]))
                return i;
        }
    }
    else
    {
        for (int i = this.startIndex; i <= this.endIndex; i++)
        {
            if (this.source[i] == null)
                return i;
        }
    }
    return -1;
}

public void Insert(int index, T item)
{
    throw new NotSupportedException();
}

public void RemoveAt(int index)
{
    throw new NotSupportedException();
}

public T this[int index]
{
    get
    {
        if (index >= 0 && index < this.count)
            return this.source[index + this.startIndex];
        else
            throw new IndexOutOfRangeException("index");
    }
    set
    {
        if (index >= 0 && index < this.count)
            this.source[index + this.startIndex] = value;
        else
            throw new IndexOutOfRangeException("index");
    }
}

#endregion

#region ICollection<T> Members

public void Add(T item)
{
    throw new NotSupportedException();
}

public void Clear()
{
    throw new NotSupportedException();
}

public bool Contains(T item)
{
    return this.IndexOf(item) >= 0;
}

public void CopyTo(T[] array, int arrayIndex)
{
    for (int i=0; i<this.count; i++)
    {
        array[arrayIndex + i] = this.source[i + this.startIndex];
    }
}

public int Count
{
    get { return this.count; }
}

public bool IsReadOnly
{
    get { return true; }
}

public bool Remove(T item)
{
    throw new NotSupportedException();
}

#endregion

#region IEnumerable<T> Members

public IEnumerator<T> GetEnumerator()
{
    for (int i = this.startIndex; i < this.endIndex; i++)
    {
        yield return this.source[i];
    }
}

#endregion

#region IEnumerable Members

IEnumerator IEnumerable.GetEnumerator()
{
    return GetEnumerator();
}

#endregion

}


4
मैं IndexOf के लिए EqualityComparer.Default का उपयोग करने का सुझाव देता हूं - इस तरह आपको किसी विशेष आवरण की आवश्यकता नहीं है।
जॉन स्कीट

1
मुझे उम्मीद है कि यह बिल्कुल ठीक होगा। मैं निश्चित रूप से पहले सरल कोड के साथ जाना होगा।
जॉन स्कीट

ऐसा कुछ मेरी राय में सबसे अच्छा तरीका है। लेकिन स्पष्ट रूप से यह एक साधारण से अधिक काम (पहली बार) है Array.Copy, भले ही इसके कई फायदे हो सकते हैं, जैसे कि सूची में प्रविष्टियों की एक प्रति के बजाय सबलिस्ट का शाब्दिक रूप से मूल सूची में एक क्षेत्र होने के नाते।
इदियाकापी


6

बाइट सरणियों के लिए System.Buffer.BlockCopy आपको बहुत अच्छा प्रदर्शन देगा।


1
जो केवल वास्तव में मायने रखता है यदि आप इसे हजारों या लाखों बार कर रहे हैं। सॉकेट एप्लिकेशन में, आप शायद कुछ इनपुट ले रहे हैं और इसे भागों में तोड़ रहे हैं। यदि आप इसे केवल एक बार कर रहे हैं, तो सबसे अच्छा प्रदर्शन वह है जो अगले प्रोग्रामर को आसानी से समझ में आ जाएगा।
माइकल ब्लैकबर्न

5

आप टेक एक्सटेंशन विधि का उपयोग कर सकते हैं

var array = new byte[] {1, 2, 3, 4};
var firstTwoItems = array.Take(2);

3

यह एक समाधान हो सकता है कि:

var result = foo.Slice(40, int.MaxValue);

फिर परिणाम एक IEnumerable <IEnumerable <बाइट >> पहले IEnumerable <बाइट के साथ> फू के पहले 40 बाइट्स होते हैं , और एक दूसरा IEnumerable <बाइट> होता है। शेष रखता है।

मैंने एक रैपर क्लास लिखा था, पूरी यात्रा आलसी है, आशा है कि यह मदद कर सकता है:

public static class CollectionSlicer
{
    public static IEnumerable<IEnumerable<T>> Slice<T>(this IEnumerable<T> source, params int[] steps)
    {
        if (!steps.Any(step => step != 0))
        {
            throw new InvalidOperationException("Can't slice a collection with step length 0.");
        }
        return new Slicer<T>(source.GetEnumerator(), steps).Slice();
    }
}

public sealed class Slicer<T>
{
    public Slicer(IEnumerator<T> iterator, int[] steps)
    {
        _iterator = iterator;
        _steps = steps;
        _index = 0;
        _currentStep = 0;
        _isHasNext = true;
    }

    public int Index
    {
        get { return _index; }
    }

    public IEnumerable<IEnumerable<T>> Slice()
    {
        var length = _steps.Length;
        var index = 1;
        var step = 0;

        for (var i = 0; _isHasNext; ++i)
        {
            if (i < length)
            {
                step = _steps[i];
                _currentStep = step - 1;
            }

            while (_index < index && _isHasNext)
            {
                _isHasNext = MoveNext();
            }

            if (_isHasNext)
            {
                yield return SliceInternal();
                index += step;
            }
        }
    }

    private IEnumerable<T> SliceInternal()
    {
        if (_currentStep == -1) yield break;
        yield return _iterator.Current;

        for (var count = 0; count < _currentStep && _isHasNext; ++count)
        {
            _isHasNext = MoveNext();

            if (_isHasNext)
            {
                yield return _iterator.Current;
            }
        }
    }

    private bool MoveNext()
    {
        ++_index;
        return _iterator.MoveNext();
    }

    private readonly IEnumerator<T> _iterator;
    private readonly int[] _steps;
    private volatile bool _isHasNext;
    private volatile int _currentStep;
    private volatile int _index;
}

2

मुझे नहीं लगता कि सी # रेंज शब्दार्थ का समर्थन करता है। आप एक विस्तार विधि लिख सकते हैं, जैसे:

public static IEnumerator<Byte> Range(this byte[] array, int start, int end);

लेकिन दूसरों की तरह आपने कहा है कि अगर आपको एक स्टार्ट इंडेक्स सेट करने की जरूरत नहीं है, तो Takeआपको बस जरूरत है।


1

यहां एक एक्सटेंशन फ़ंक्शन है जो जेनेरिक का उपयोग करता है और PHP फ़ंक्शन array_slice की तरह व्यवहार करता है । नकारात्मक ऑफसेट और लंबाई की अनुमति है।

public static class Extensions
{
    public static T[] Slice<T>(this T[] arr, int offset, int length)
    {
        int start, end;

        // Determine start index, handling negative offset.
        if (offset < 0)
            start = arr.Length + offset;
        else
            start = offset;

        // Clamp start index to the bounds of the input array.
        if (start < 0)
            start = 0;
        else if (start > arr.Length)
            start = arr.Length;

        // Determine end index, handling negative length.
        if (length < 0)
            end = arr.Length + length;
        else
            end = start + length;

        // Clamp end index to the bounds of the input array.
        if (end < 0)
            end = 0;
        if (end > arr.Length)
            end = arr.Length;

        // Get the array slice.
        int len = end - start;
        T[] result = new T[len];
        for (int i = 0; i < len; i++)
        {
            result[i] = arr[start + i];
        }
        return result;
    }
}

1
बहुत अच्छा है, हालाँकि .NET दुनिया की कुछ बातें। यदि start0 और के बीच नहीं है arr.Length, तो इसे संभवतः सीमा अपवाद से बाहर फेंक देना चाहिए। इसके अलावा, end >= start >= 0इसलिए आपको जांच करने की आवश्यकता नहीं है end < 0, यह संभव नहीं है कि ऐसा होगा। आप शायद इसे और भी बेहतर तरीके से जाँच कर सकते हैं length >= 0और फिर len = Math.min(length, arr.Length - start)इसके बजाय फडलिंग के साथ end
मैथ्यू शेर्ले

0
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace data_seniens
{
    class Program
    {
        static void Main(string[] args)
        {
            //new list
            float [] x=new float[]{11.25f,18.0f,20.0f,10.75f,9.50f, 11.25f, 18.0f, 20.0f, 10.75f, 9.50f };

            //variable
            float eat_sleep_area=x[1]+x[3];
            //print
            foreach (var VARIABLE in x)
            {
                if (VARIABLE < x[7])
                {
                    Console.WriteLine(VARIABLE);
                }
            }



            //keep app run
        Console.ReadLine();
        }
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.