1

JavaScript スパース配列を C# 表現にマップしようとしています。

これを行うための推奨される方法は何ですか?

元の配列に値が含まれていたオリディナルのリストを含む辞書を使用することを検討しています。

他のアイデアはありますか?

ありがとう!

4

2 に答える 2

1

ノート

私は、このシナリオに対する 2 つの .NET ソリューションを考え出しました。どちらのソリューションも同じ JavaScript コードを必要とするため、この回答には 2 つのソリューションの JavaScript コードのみを含めました。

では回答へ…


編集注:最初はあなたの質問を完全に理解していなかったことをお詫びします。以前に使用された「スパース配列」という用語を聞いたことがなかったので、それを調べてWikipedia からテキストブックの定義を見つけなければなりませんでした。これは、あなたが説明していたものとはまったく異なります。 、あなたが説明していたものではないようです。ただし、このように使用することには意味があります。

このシナリオについてさらに考えると、解決策を思いつくようになりました。おっしゃったように、C# (および一般的な .NET) にはundefinednull.

私が理解していることから、あなたは配列で3つの異なるものを表現できるようになりたいと考えています:

  • 配列要素の有効な値
  • 配列要素の null 値
  • 「未定義」の配列要素の概念

ご指摘のとおり、C# には「未定義」という概念がありませんnull。.NET に対してundefinedは、 と同じでnullあり、おそらくそのままにしておく必要があります。しかし、とにかくこれを表現する方法が明らかに必要なので (私は奇妙なビジネス ルールを想定しています)、動作する実装を考え出そうとしました。ただし、これを行わないことを強くお勧めします。

JavaScript 側

最初の課題は、JavaScript で配列をシリアライズすることです。配列を JSON 形式に変換すると、配列undefined内の要素は自動的に に変換されnullます。を呼び出すとJSON.stringify(...)、次のようになります。

var array = [73,42,undefined,null,23];
var json = JSON.stringify(array);
if(json === "[73,42,null,null,23]")
    alert('I am always true!');

上記の例は、配列内の未定義の要素が " null" としてシリアル化されることを示しています。本当に undefined を格納する必要がある場合は、配列を手動でシリアライズして未定義の要素を持つ変換メソッドを作成する必要があります。理想的なソリューションは、次の JSON になります。

"[73,42,undefined,null,23]"

ただし、これは機能しません。.NET も、この文字列を解析することもできませんJavaScriptSerializer(JSON.parse(...)ただし、JavaScript のeval(...)方法機能します)。undefined必要なのは、標準の JSON メソッドを使用してシリアル化され、.NET の .NET で理解できる" " を表す方法JavaScriptSerializerです。これを行う秘訣は、未定義を表す実際のオブジェクトを考え出すことです。これを表すために、次の JSON 文字列を使用しました。

"{undefined:null}"

唯一のプロパティが「未定義」と呼ばれ、値が であるオブジェクトnull。これは、JavaScript コードのどのオブジェクトにも存在しない可能性が高いため、存在しない「未定義」配列要素を表すのに十分な「一意」のフラグであると考えているため、これを使用しています。

このように JSON をシリアライズするには、ジョブを実行するリプレースを提供する必要があるため、その方法は次のとおりです。

var arr = [73,42,undefined,null,23];
var json = JSON.stringify(arr, function(key, value) {
    jsonArray = value;
    if(value instanceof Array) {    
        var jsonArray = "[";
        for(var i = 0; i < value.length; i++) {
            var val = value[i];
            if(typeof val === "undefined") {
                jsonArray += "{undefined:null}";
            } else {
                jsonArray += JSON.stringify(value[i]);
            }
            if(i < value.length - 1) {
                jsonArray += ",";
            }
        }
        jsonArray += "]";
        if(key != null && key != "") {
            return key + ":" + jsonArray;
        } else {
            return jsonArray;
        }
    }
    
    if(key != null && key != "") {
        return key + ":" + JSON.stringify(jsonArray);
    } else {
        return JSON.stringify(jsonArray);
    }
});

これにより、次のような JSON 文字列が取得されます。

"[73,42,{undefined:null},null,23]"

これの欠点は、逆シリアル化されると、undefined配列要素が ではなくundefined、別のオブジェクトになることです。これは、パーサーがこれを特別に処理する必要があることを意味します。完全に作成された「未定義」オブジェクトを認識しない JSON パーサーでこれを使用しないようにしてください。次のステップは、C# で処理することです。


C# 側

ここでの課題は、未定義の値をどのように表現するかです。以下の解決策は、未定義のインデックスを配列内に「存在しない」ものとして扱うことを選択します。私の他の解決策は、構造体のラッパーに似たラッパーを作成しますNullable<T>

SparseArray<T>ディクショナリ ラッパーを作成する

SparseArray<T>.NET で通常の配列と同じように使用できるクラスを作成しました。このクラスと通常の配列の違いは、未定義の配列要素にアクセスすると、 custom がスローされることIndexNotFoundExceptionです。この例外のスローを回避するには、要素SparseArray<T>.ContainsIndex(index)にそのインデックスがあるかどうかを確認するために呼び出して要素が定義されているかどうかを最初に確認します。

質問で述べたように、内部的には辞書を使用します。初期化すると、まずコンストラクターが JSON 文字列を受け取り、JavaScriptSerializer を介して実行します。

次に、デシリアライズされた配列を取得し、各配列要素をその_Arrayディクショナリに追加し始めます。要素を追加すると{undefined:null}、JavaScript で「文字列化」したときに定義したオブジェクトが検索されます (過去形が正しいかどうかはわかりません...)。このオブジェクトが検出された場合、インデックスはスキップされます。配列の長さは、未定義の値が見つかると増加しますが、それらのインデックスはスキップされます。

クラスのコードはかなり長いので、最初に使用例を示します。

string json = "[4,5,null,62,{undefined:null},1,68,null, 3]";
SparseArray<int?> arr = new SparseArray<int?>(json);

ループではfor { }、配列にアクセスする前に、配列に各インデックスが含まれているかどうかを確認する必要があります。ループでは、列挙子は定義された値のみを保持するforeach { }ため、値の発生について心配する必要はありませんundefined

これが私のSparseArray<T>クラスのコードです:

[DebuggerDisplay("Count = {Count}")]
public class SparseArray<T>: IList<T>
{
    Dictionary<int, T> _Array = new Dictionary<int, T>();
    int _Length = 0;

    public SparseArray(string jsonArray)
    {
        var jss = new JavaScriptSerializer();
        var objs = jss.Deserialize<object[]>(jsonArray);
        
        for (int i = 0; i < objs.Length; i++)
        {
            if (objs[i] is Dictionary<string, object>)
            {
                // If the undefined object {undefined:null} is found, don't add the element
                var undefined = (Dictionary<string, object>)objs[i];
                if (undefined.ContainsKey("undefined") && undefined["undefined"] == null)
                {
                    _Length++;
                    continue;
                }
            }
            T val;
            // The object being must be serializable by the JavaScriptSerializer
            // Or at the very least, be convertible from one type to another
            // by implementing IConvertible.
            try
            {
                val = (T)objs[i];
            }
            catch (InvalidCastException)
            {
                val = (T)Convert.ChangeType(objs[i], typeof(T));
            }

            _Array.Add(_Length, val);
            _Length++;
        }
    }

    public SparseArray(int length)
    {
        // Initializes the array so it behaves the same way as a standard array when initialized.
        for (int i = 0; i < length; i++)
        {
            _Array.Add(i, default(T));
        }
        _Length = length;
    }

    public bool ContainsIndex(int index)
    {
        return _Array.ContainsKey(index);
    }


    #region IList<T> Members

    public int IndexOf(T item)
    {
        foreach (KeyValuePair<int, T> pair in _Array)
        {
            if (pair.Value.Equals(item))
                return pair.Key;
        }
        return -1;
    }

    public T this[int index]
    {
        get {
            if (_Array.ContainsKey(index))
                return _Array[index];
            else
                throw new IndexNotFoundException(index);
        }
        set { _Array[index] = value; }
    }              

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

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

    #endregion

    #region ICollection<T> Members

    public void Add(T item)
    {
        _Array.Add(_Length, item);
        _Length++;
    }

    public void Clear()
    {
        _Array.Clear();
        _Length = 0;
    }

    public bool Contains(T item)
    {
        return _Array.ContainsValue(item);
    }        

    public int Count
    {
        get { return _Length; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

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

    public void CopyTo(T[] array, int arrayIndex)
    {
        throw new NotImplementedException();
    }

    #endregion

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator()
    {
        return _Array.Values.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _Array.Values.GetEnumerator();
    }

    #endregion
}

そして、このクラスを使用するために必要な例外:

public class IndexNotFoundException:Exception
{
    public IndexNotFoundException() { }
    public IndexNotFoundException(int index) 
          : base(string.Format("Array is undefined at position [{0}]", index)) 
    { 
    }
}
于 2009-09-26T14:04:57.380 に答える
0

ノート

私は、このシナリオに対する 2 つの .NET ソリューションを考え出しました。どちらのソリューションがより有利であるかに基づいて投票できるように、また著者が最も有利なソリューションを「承認された回答」として選択できるように、それらを個別の回答にまとめました。どちらのソリューションも同じ JavaScript コードを必要とするため、他の回答には 2 つのソリューションの JavaScript コードのみを含めました。この回答が承認済みとしてマークされている場合は、この回答に JavaScript ソリューションを含めて、ページで論理的に最初に来る回答に含まれるようにします。

では答えを…。


まず、言及することが重要であるため、他のソリューションで言及したことを繰り返したいと思います。

ご指摘のとおり、C# には「未定義」という概念がありませんnull。.NET に対してundefinedは、 と同じでnullあり、おそらくそのままにしておく必要があります。しかし、とにかくこれを表現する方法が明らかに必要なので (私は奇妙なビジネス ルールを想定しています)、動作する実装を考え出そうとしました。ただし、これを行わないことを強くお勧めします。

このソリューションでは、 というラッパー クラスを作成しましたUndefined<T>。.NET のネイティブ と同様に機能しますNullable<T>。これの欠点は、.NET には「未定義」の概念がないため、オブジェクトの値へのアクセスを処理する方法を決定するのが難しいことです。undefinedのときにオブジェクトをキャストしようとすると、例外をスローすることにしましたundefined

このソリューションでは、すべての要素が存在する実際の配列があります。すべての要素が存在しますが、実際にはすべての要素の値を取得することはできません。私が作成したクラスはNullable<T>、未定義のオブジェクトをキャストしようとしたり、その を取得しようとするとValue、このクラスが例外をスローすることを除いて、あらゆる点で同様に動作します。実際にIsDefined使用する前に、使用できることを確認するために電話する必要があります。Value

[DebuggerDisplay("IsDefined:{IsDefined}, Value:{_Value}")]
public sealed class Undefined<T>
{
    public static Undefined<T>[] DeserializeArray(string jsonArray)
    {
        var jss = new JavaScriptSerializer();
        var objs = jss.Deserialize<object[]>(jsonArray);
        var undefinedArray = new Undefined<T>[objs.Length];

        for (int i = 0; i < objs.Length; i++)
        {
            if (objs[i] is Dictionary<string, object>)
            {
                var undefined = (Dictionary<string, object>)objs[i];
                if (undefined.ContainsKey("undefined") && undefined["undefined"] == null)
                {
                    undefinedArray[i] = new Undefined<T>(default(T), false);
                    continue;
                }
            }
            T val;
            // The object being must be serializable by the JavaScriptSerializer
            // Or at the very least, be convertible from one type to another
            // by implementing IConvertible.
            try
            {
                val = (T)objs[i];
            }
            catch (InvalidCastException)
            {
                val = (T)Convert.ChangeType(objs[i], typeof(T));
            }

            undefinedArray[i] = new Undefined<T>(val, true);
        }

        return undefinedArray;

    }

    private Undefined(T value, bool isDefined)
    {
        Value = value;
        IsDefined = isDefined;
    }

    public static explicit operator T(Undefined<T> value)
    {
        if (!value.IsDefined)
            throw new InvalidCastException("Value is undefined. Unable to cast.");
        return value.Value;
    }

    public bool IsDefined { get; private set; }

    private T _Value;
    public T Value
    {
        get
        {
            if (IsDefined)
                return _Value;
            throw new Exception("Value is undefined.");
        }
        private set { _Value = value; }
    }

    public override bool Equals(object other)
    {
        Undefined<T> o = other as Undefined<T>;
        if (o == null)
            return false;
        if ((!this.IsDefined && o.IsDefined) || this.IsDefined && !o.IsDefined)
            return false;
        return this.Value.Equals(o.Value);
    }

    public override int GetHashCode()
    {
        if (IsDefined)
            return Value.GetHashCode();
        return base.GetHashCode();
    }

    public T GetValueOrDefault()
    {
        return GetValueOrDefault(default(T));
    }

    public T GetValueOrDefault(T defaultValue)
    {
        if (IsDefined)
            return Value;
        return defaultValue;
    }

    public override string ToString()
    {
        if (IsDefined)
            Value.ToString();
        return base.ToString();
    }
} 
于 2009-09-27T20:25:44.810 に答える