[英]How can I replicate the behavior of Python's sorted built-in function in C#?
我在Python中有一個字典列表。 此列表作為Web服務之間的json傳遞。 這些Web服務基於傳遞的json創建獨特的簽名。 創建簽名的一部分是規范化數據有效負載並確保所有內容都處於正確的順序,所以我這樣做(在Python中) - 工作正常。
data = [{'a': '1', 'b': '2', 'c': 3}, {'d': 3}, {3: 1}, {'100': '200'}]
sorted(data)
> [{3: 1}, {'100': '200'}, {'d': 3}, {'a': '1', 'c': 3, 'b': '2'}]
現在,我需要在混合中添加一個C#應用程序,它需要能夠創建與Python代碼完全相同的簽名。 我沒有發現以與Python的sorted
內置函數相同的方式對上述數據結構進行排序的秘訣。
我正在使用ServiceStack來解析json數據。
我希望它會像做這樣的事情一樣容易(在C#中):
var jsonPayload = "[{\"a\": \"1\", \"b\": \"2\", \"c\": 3}, {\"d\": 3}, {3: 1}, {\"100\": \"200\"}]";
var parsedJson = JsonArrayObjects.Parse(jsonPayload);
parsedJson.Sort();
但是,我從上面的C#代碼中得到了這個例外:
`At least one object just implement IComparable`
我理解為什么我會收到這個錯誤,但我不確定我應該怎么做。 我真的希望我不必滾動自己的排序邏輯。 我正在處理的實際數據非常動態。 這只是阻止我向前發展的一個例子。
有沒有人有任何建議或建議如何在C#中進行排序,就像這種嵌套數據結構的sorted
python函數一樣?
謝謝!
示例數據無效JSON,如圖所示。 整數三不能是鍵,它必須是一個字符串。 將{3: 1}
更改為{"3": 1}
第二個問題是默認情況下C#字典不可訂購。 但是,您可以將它們子類化以使它們可訂購。
用於排序字典的Python2.x算法是:
1)如果詞典的大小不同,則長度較短的詞是較小的值。
2)如果大小相同,則掃描第一個字典以找到第一個字典中的最小鍵,該鍵不存在或在第二個字典中具有不匹配的值。 不匹配的值確定哪個字典最大。
以下是Objects / dictobject.c的Python2.7源代碼的相關摘錄:
/* Subroutine which returns the smallest key in a for which b's value
is different or absent. The value is returned too, through the
pval argument. Both are NULL if no key in a is found for which b's status
differs. The refcounts on (and only on) non-NULL *pval and function return
values must be decremented by the caller (characterize() increments them
to ensure that mutating comparison and PyDict_GetItem calls can't delete
them before the caller is done looking at them). */
static PyObject *
characterize(PyDictObject *a, PyDictObject *b, PyObject **pval)
{
PyObject *akey = NULL; /* smallest key in a s.t. a[akey] != b[akey] */
PyObject *aval = NULL; /* a[akey] */
Py_ssize_t i;
int cmp;
for (i = 0; i <= a->ma_mask; i++) {
PyObject *thiskey, *thisaval, *thisbval;
if (a->ma_table[i].me_value == NULL)
continue;
thiskey = a->ma_table[i].me_key;
Py_INCREF(thiskey); /* keep alive across compares */
if (akey != NULL) {
cmp = PyObject_RichCompareBool(akey, thiskey, Py_LT);
if (cmp < 0) {
Py_DECREF(thiskey);
goto Fail;
}
if (cmp > 0 ||
i > a->ma_mask ||
a->ma_table[i].me_value == NULL)
{
/* Not the *smallest* a key; or maybe it is
* but the compare shrunk the dict so we can't
* find its associated value anymore; or
* maybe it is but the compare deleted the
* a[thiskey] entry.
*/
Py_DECREF(thiskey);
continue;
}
}
/* Compare a[thiskey] to b[thiskey]; cmp <- true iff equal. */
thisaval = a->ma_table[i].me_value;
assert(thisaval);
Py_INCREF(thisaval); /* keep alive */
thisbval = PyDict_GetItem((PyObject *)b, thiskey);
if (thisbval == NULL)
cmp = 0;
else {
/* both dicts have thiskey: same values? */
cmp = PyObject_RichCompareBool(
thisaval, thisbval, Py_EQ);
if (cmp < 0) {
Py_DECREF(thiskey);
Py_DECREF(thisaval);
goto Fail;
}
}
if (cmp == 0) {
/* New winner. */
Py_XDECREF(akey);
Py_XDECREF(aval);
akey = thiskey;
aval = thisaval;
}
else {
Py_DECREF(thiskey);
Py_DECREF(thisaval);
}
}
*pval = aval;
return akey;
Fail:
Py_XDECREF(akey);
Py_XDECREF(aval);
*pval = NULL;
return NULL;
}
static int
dict_compare(PyDictObject *a, PyDictObject *b)
{
PyObject *adiff, *bdiff, *aval, *bval;
int res;
/* Compare lengths first */
if (a->ma_used < b->ma_used)
return -1; /* a is shorter */
else if (a->ma_used > b->ma_used)
return 1; /* b is shorter */
/* Same length -- check all keys */
bdiff = bval = NULL;
adiff = characterize(a, b, &aval);
if (adiff == NULL) {
assert(!aval);
/* Either an error, or a is a subset with the same length so
* must be equal.
*/
res = PyErr_Occurred() ? -1 : 0;
goto Finished;
}
bdiff = characterize(b, a, &bval);
if (bdiff == NULL && PyErr_Occurred()) {
assert(!bval);
res = -1;
goto Finished;
}
res = 0;
if (bdiff) {
/* bdiff == NULL "should be" impossible now, but perhaps
* the last comparison done by the characterize() on a had
* the side effect of making the dicts equal!
*/
res = PyObject_Compare(adiff, bdiff);
}
if (res == 0 && bval != NULL)
res = PyObject_Compare(aval, bval);
Finished:
Py_XDECREF(adiff);
Py_XDECREF(bdiff);
Py_XDECREF(aval);
Py_XDECREF(bval);
return res;
}
您可以解決它:從C#調用Python排序函數,因此您將具有完全相同的行為。
你可以使用IronPython :
Python代碼:
def Simple():
print "Hello from Python"
print "Call Dir(): "
print dir()
C#代碼:
using System;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
public class dynamic_demo
{
static void Main()
{
var ipy = Python.CreateRuntime();
dynamic test = ipy.UseFile("Test.py");
test.Simple();
}
}
在這里完成示例。
http://blogs.msdn.com/b/charlie/archive/2009/10/25/hosting-ironpython-in-ac-4-0-program.aspx
另一個選擇是從C#app中的命令行調用Python排序函數。 您可以使用此方法:
private void run_cmd(string cmd, string args)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = "my/full/path/to/python.exe";
start.Arguments = string.Format("{0} {1}", cmd, args);
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using(Process process = Process.Start(start))
{
using(StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
}
有關此內容的更多信息,請參見: 如何從C#運行Python腳本?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.