1

I'm looking to create a Vec<Vec<String>> from a HashMap<String, Value>. The Value type is from serde_json.

The expected output is:

[
 ["name"]
 ["age"]
 ["phones"]
 ["phones", "0"]
 ["phones", "1"]
 ["friends"]
 ["friends", "0"]
 ["friends", "0", "name"]
 ["friends", "0", "age"]
 ["friends", "0", "phones"]
 ["friends", "0", "phones", "0"]
 ["friends", "0", "phones", "1"]
 ["friends", "0", "friends"]
 ["friends", "0", "friends", "0"]
 ["friends", "0", "friends", "0", "name"]
 ["friends", "0", "friends", "0", "age"]
 ["friends", "0", "friends", "0", "phones"]
 ["friends", "0", "friends", "0", "phones", "0"]
 ["friends", "0", "friends", "0", "friends"]
 ["friends", "0", "friends", "1"]
 ["friends", "0", "friends", "1", "name"]
 ["friends", "0", "friends", "1", "age"]
 ["friends", "0", "friends", "1", "phones"]
 ["friends", "0", "friends", "1", "phones", "0"]
 ["friends", "0", "friends", "1", "friends"]
 ["friends", "0", "friends", "2"]
 ["friends", "0", "friends", "2", "name"]
 ["friends", "0", "friends", "2", "age"]
 ["friends", "0", "friends", "2", "phones"]
 ["friends", "0", "friends", "2", "phones", "0"]
 ["friends", "0", "friends", "2", "friends"]
];

Question: How to iterate over all keys in a recursive / nested hashmap and match the expect output above?

use serde_json::{Result, Value};
use std::collections::HashMap;

fn deep_keys(data: HashMap<String, Value>) -> Vec<Vec<String>>{
    let mut vec = Vec::new();
    vec.push(Vec::new());

    // logic

    for (str, val) in data {
        match &val {
            Value::String(string) => vec[0].push(str),
            Value::Number(Number) => vec[0].push(str),
            Value::Bool(bool) => vec[0].push(str),
            _ => {
                let mut map = HashMap::new();
                for (innerStr, innerVal) in val {
                    let mut map = HashMap::new();
                    map.insert(str, innerVal);
                    deep_keys(map);
                }

            }
        };
    }
    vec
}

fn main() {
    let data = r#"
    {
        "name": "John Doe",
        "age": 43,
        "phones": [
            "+44 1234567",
            "+44 2345678"
        ],
        "friends": [
            {
                "name": "Jenn Doe",
                "age": 15,
                "phones": ["+44 1234567", "+44 2345678"],
                "friends": [
                    {
                        "name": "Richard Carter",
                        "age": 20,
                        "phones": ["+1 1234567"],
                        "friends": []
                    },
                      {
                        "name": "Angus Yik",
                        "age": 21,
                        "phones": ["+1 1234567"],
                        "friends": []
                    },
                    {
                        "name": "Diana White",
                        "age": 24,
                        "phones": ["+1 12345673"],
                        "friends": []
                    }
                ]
            }
        ]
    }"#;
    let v: HashMap<String, Value> = serde_json::from_str(data).expect("error");
    println!("{:?}", deep_keys(v));
}

When I try to make deep_keys recursive, it reports an error about mismatching types that I'm not sure how to fix:

rror[E0277]: `serde_json::value::Value` is not an iterator
  --> src/main.rs:17:45
   |
17 |                 for (innerStr, innerVal) in val {
   |                                             ^^^ `serde_json::value::Value` is not an iterator
   |
   = help: the trait `std::iter::Iterator` is not implemented for `serde_json::value::Value`
   = note: required by `std::iter::IntoIterator::into_iter`
Armeen Moon
  • 18,061
  • 35
  • 120
  • 233
  • @Shepmaster Sorry I tried to simplify the json for the question and made a typo https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=89478f07b88bd06fc10a5a648688244c – Armeen Moon Jul 30 '19 at 15:21
  • You've tagged this as [tag:recursion], but I don't see any attempts at a recursive function. What happens if you try one? – Shepmaster Jul 30 '19 at 15:22
  • @Shepmaster I tried recursion before but I wasn't sure if that was the only way to solve and I couldn't get it to work. I've updated the answer with my recursive attempt but I didn't get very far because of the coercing/static types. – Armeen Moon Jul 30 '19 at 15:40
  • if it helps im trying to recreate https://github.com/deoxxa/dotty/blob/master/lib/index.js#L234 in rust – Armeen Moon Jul 30 '19 at 15:45
  • See the lines like `if (typeof object[k] === "object" && object[k] !== null)`? Replace those with pattern matching. – Shepmaster Jul 30 '19 at 15:53
  • interesting! Is "object" now Value ? – Armeen Moon Jul 30 '19 at 15:54
  • No, but it's one of the variants of [`Value`](https://docs.rs/serde_json/1.0.40/serde_json/enum.Value.html). – Shepmaster Jul 30 '19 at 15:56
  • This is awesome is there a equivalent to concat in rust? – Armeen Moon Jul 30 '19 at 16:09
  • [How do I concatenate strings?](https://stackoverflow.com/q/30154541/155423); [Best way to concatenate vectors in Rust](https://stackoverflow.com/q/40792801/155423); – Shepmaster Jul 30 '19 at 16:14
  • @Shepmaster is there any way I can get more help... I'm still struggling to accomplish the pattern matching w/ recusion. Static typing is messing me up. – Armeen Moon Jul 30 '19 at 18:35

1 Answers1

2
use serde_json::{Value};
use std::fs;
//use std::path::Path;
use std::io::Read;

fn deep_keys(value: &Value, current_path: Vec<String>, output: &mut Vec<Vec<String>>) {
    if current_path.len() > 0 {
        output.push(current_path.clone());
    }

    match value {
        Value::Object(map) => {
            for (k, v) in map {
                let mut new_path = current_path.clone();
                new_path.push(k.to_owned());
                deep_keys(v,  new_path, output);

            }
        },
        Value::Array(array) => {
            for (i, v) in array.iter().enumerate() {
                let mut new_path = current_path.clone();
                new_path.push(i.to_string().to_owned());
                deep_keys(v,  new_path, output);
            }
        },
        _ => ()
    }
}

fn main() {
    let mut output = vec![vec![]];
    let current_path = vec![];
    let mut file = fs::File::open("src/geo.json").unwrap();
    let mut data = String::new();
    file.read_to_string(&mut data).unwrap();

    let value:Value = serde_json::from_str(&data).expect("error");
    deep_keys(&value, current_path, &mut output);
    println!("{:?}", output);
}
Armeen Moon
  • 18,061
  • 35
  • 120
  • 233