检测JSON数组中没有重复项的最快正确方法是什么?

检测JSON数组中没有重复项的最快正确方法是什么?,json,algorithm,rust,serde,Json,Algorithm,Rust,Serde,我需要检查serde_json::Value数组中的所有项是否唯一。由于此类型不实现哈希,因此我提出了以下解决方案: use serde_json::{json, Value}; use std::collections::HashSet; fn is_unique(items: &[Value]) -> bool { let mut seen = HashSet::with_capacity(items.len()); for item in items.ite

我需要检查
serde_json::Value
数组中的所有项是否唯一。由于此类型不实现
哈希
,因此我提出了以下解决方案:

use serde_json::{json, Value};
use std::collections::HashSet;

fn is_unique(items: &[Value]) -> bool {
    let mut seen = HashSet::with_capacity(items.len());
    for item in items.iter() {
        if !seen.insert(item.to_string()) {
            return false;
        }
    }
    true
}

fn main() {
    let value1 = json!([1, 2]);
    assert!(is_unique(&value1.as_array().unwrap()));
    let value2 = json!([1, 1]);
    assert!(!is_unique(&value2.as_array().unwrap()));
}
我假设只有当
serde_json
是使用
preserve_order
功能构建的(每次都以相同的顺序序列化对象)时,它才应该工作,但我不是100%确定

主要用法上下文

JSON模式验证。“”关键字实现

相关用例

JSON阵列的重复数据消除,以优化JSON模式推断

例如,输入数据是
[1,2,{“foo”:“bar”}]
。简单的推断可能会得出以下结果:

{
    "type": "array", 
    "items": {
        "anyOf": [
            {"type": "integer"}, 
            {"type": "integer"},
            {"type": "object", "required": ["foo"]}
        ]
    }
}
项/anyOf
中的值只能减少为两个值

问题:检查任意JSON数组中是否存在重复项的最省时、最正确的方法是什么

我使用了
serde_json=“1.0.48”

锈蚀:1.42.0


取决于JSON数组是否已排序。如果已排序,则可以使用二进制搜索检查该值是否与其他值匹配。要排序,可以使用合并排序。总复杂度为O(nlogn+logn)。或者可以按顺序迭代并检查重复的行O(n^2)

将每个数组项转换为字符串相当昂贵–它要求每个项至少分配一个字符串,而且很可能不止这些。也很难确保映射(或JSON语言中的“对象”)以规范形式表示

一个更快、更健壮的替代方案是为
自己实现
哈希
。您需要定义一个新类型包装器,因为您不能在外部类型上实现外部特征。下面是一个简单的示例实现:

use serde_json::Value;
use std::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;

#[derive(PartialEq)]
struct HashValue<'a>(pub &'a Value);

impl Eq for HashValue<'_> {}

impl Hash for HashValue<'_> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        use Value::*;
        match self.0 {
            Null => state.write_u32(3_221_225_473), // chosen randomly
            Bool(ref b) => b.hash(state),
            Number(ref n) => {
                if let Some(x) = n.as_u64() {
                    x.hash(state);
                } else if let Some(x) = n.as_i64() {
                    x.hash(state);
                } else if let Some(x) = n.as_f64() {
                    // `f64` does not implement `Hash`. However, floats in JSON are guaranteed to be
                    // finite, so we can use the `Hash` implementation in the `ordered-float` crate.
                    ordered_float::NotNan::new(x).unwrap().hash(state);
                }
            }
            String(ref s) => s.hash(state),
            Array(ref v) => {
                for x in v {
                    HashValue(x).hash(state);
                }
            }
            Object(ref map) => {
                let mut hash = 0;
                for (k, v) in map {
                    // We have no way of building a new hasher of type `H`, so we
                    // hardcode using the default hasher of a hash map.
                    let mut item_hasher = DefaultHasher::new();
                    k.hash(&mut item_hasher);
                    HashValue(v).hash(&mut item_hasher);
                    hash ^= item_hasher.finish();
                }
                state.write_u64(hash);
            }
        }
    }
}
使用serde_json::Value;
使用std::hash::{hash,Hasher};
使用std::collections::hash_map::DefaultHasher;
#[衍生(部分)]
结构哈希值{
fn散列(&self,state:&mut H){
使用值::*;
匹配self.0{
Null=>state.write_u32(3_221_225_473),//随机选择
Bool(ref b)=>b.hash(state),
编号(参考编号)=>{
如果让一些(x)=n.as_64(){
x、 散列(状态);
}如果让一些(x)=n.as_i64(){
x、 散列(状态);
}如果让一些(x)=n.as_f64(){
//`f64`不实现`Hash`。但是,JSON中的浮点值保证为
//有限,因此我们可以在“有序浮点”板条箱中使用“哈希”实现。
有序浮点数::NotNan::新建(x).unwrap().hash(状态);
}
}
字符串(ref s)=>s.hash(state),
阵列(参考v)=>{
对于v中的x{
HashValue(x)、hash(state);
}
}
对象(参考地图)=>{
让mut hash=0;
对于地图中的(k,v){
//我们无法构建类型为'H'的新哈希器,因此
//使用哈希映射的默认哈希器进行硬编码。
让mut item_hasher=DefaultHasher::new();
k、 散列(&mut item_散列器);
HashValue(v).hash(&mut item_hasher);
hash^=item_hasher.finish();
}
state.write_u64(散列);
}
}
}
}
None
的值是随机选择的,以避免与其他条目发生冲突。为了计算浮点数的散列,我使用了
ordered float
板条箱。对于映射,代码为每个键/值对计算一个散列,并将这些散列简单地异或在一起,这与顺序无关。不幸的是,我们需要硬编码用于对映射项进行散列的散列程序。我们可以通过定义自己版本的
Hash
trait将其抽象出来,然后从自定义的
Hash
trait派生出
std::Hash::Hash
的具体实现,但这会使代码复杂化很多,所以除非您需要,否则我不会这样做


我们无法导出
Eq
,因为
Value
没有实现
Eq
。但是,我认为这只是一个疏忽,所以我(PR已经被接受,因此它将在未来的某个版本中发布)。

您知道切片中项目的类型吗?任意值,即任何
serde_json::Value
实例,数组未排序。如何定义映射的相等性?键/值对相等,键是字符串,值-任何其他
。空映射是相等的。所以顺序不重要吗?我认为最快的解决方案是为
Value
在newtype包装上实现快速哈希,这样您就可以直接在该包装上使用
HashSet
,而无需转换为字符串。对于映射,您需要基于键/值对的散列实现一个顺序无关的散列–请参阅。如果数组已排序,则不使用二进制搜索–只需在数组上迭代并检查是否有任何项等于上一项。在排序数组中,重复项是相邻的。但是,这要求首先对项目进行排序,而问题没有给出它们可以排序的指示。如果集合是异构的,这将如何工作?@Stargateur It操作于
Value
,这是一个任意的JSON值。是的,但为什么数字不能与字符串冲突?@Stargateur Hashes总是会冲突。我不明白你的意思。@Stranger6667我真的不确定所有有限浮点数是否都有唯一的表示,尤其是非规范数。在考虑了一下表示之后,我现在相信在IEEE-754-200中