How to list key-value properties sorted alphabetically

I am building a vocab list. I like to keep it basically in a huge dump, where every time I encounter a new word I add a new line

term:: definition

I would then like to have a query which will give me all the words I have in alphabetical order (otherwise this word dump becomes a complete mess).
How can I do it?
ChatGPT suggests the following code which does not work:

const file = dv.page("your-file-name.md");

if (file) {
    // Collect only the keys that come from YAML frontmatter.
    const frontmatterKeys = Object.keys(file)
        .filter(key => !key.startsWith("file.")) // Exclude all internal 'file' metadata fields
        .sort(); // Sort keys alphabetically

    // Display each key-value pair without duplicates.
    frontmatterKeys.forEach(key => {
        dv.paragraph(`${key}: ${file[key]}`);
    });
} else {
    dv.paragraph("File not found or has no properties.");
}

Another approach is to sort the list itself, selecting all and using a command from (for example) the Sort & Permute Lines plugin.

1 Like

Oh, that’s actually a great idea! I didn’t know one could do that.

I tried out your code, and it worked perfectly fine for me. What was your error? Was it?

File not found or has no properties.

If so, you need to replace the section of code that says "your-file-name.md" with your vocabulary note file name, including the path.

If you want this code in the same note as where you currently have the vocabulary definitions, you could try the following. This code automatically uses the file it currently resides in. No need to edit the file path. I also modified the code to place the definitions inside a table instead of in a list.

```dataviewjs
const file = dv.page(dv.current().file.path);
let vocab_list = [];
if (file) {
    // Collect all Dataview keys.
    const fileKeys = Object.keys(file)
        .filter(key => !key.startsWith("file.")) // Exclude all internal 'file' metadata fields
        .sort(); // Sort keys alphabetically

    fileKeys.forEach(key => {
	    let definition = file[key];
	    let def_type = typeof(definition);
	    if (!def_type.match('object')){
			vocab_list.push([`${key}`, definition]);
		}
    });
    dv.table(["Word","Definition"],vocab_list);
} else {
    dv.paragraph("File not found or has no properties.");
}

Oh, I was actually using it for the same file:)) For some reason this code does not sort the items alphabetically. But, your code outputs a table, and in the table it is possible to sort by any column - that does work. And your code also excludes duplicates by removing the internal fields, so I think that is a working solution, thank you!

1 Like

One thing I just noticed is that if you use uppercase letters or spaces in your words, you end up creating a duplicate normalized Dataview name for the key.

https://blacksmithgu.github.io/obsidian-dataview/resources/faq/

For example…
This is a very long key:: Testing

results in two fields…
This is a very long key : Testing
this-is-a-very-long-key : Testing

The first field doesn’t end up sorting properly with my original code. I don’t currently know of a way to only display one of these fields. The easy solution is to only use lower case words for your keys with no spaces. I don’t know how practical this would be for your use case.

I really liked your solution of just sorting the table instead! Very nice!

I also did the following to pad out my definition column a bit more. Just another option.

dv.table(["Word","<span style='padding-right:500px;'>Definition</span>"],vocab_list);

I modified my code to remove keys from the frontmatter, ignore normalized Dataview keys and to sort.

```dataviewjs
const file = dv.page(dv.current().file.path);
let vocab_list = [];
let norm_map = new Map(); // Use a Map to track normalized Keys

if (file) {
    // Collect all Dataview keys.
    const fileKeys = Object.keys(file);
    fileKeys.forEach(key => {
	    let key_normalized = key.toLowerCase().replaceAll(/ /g,"-");
	    let definition = file[key];
	    // check if key is in frontmatter or a normalize key
	    if (!(key.match("file") || (key in file["file"].frontmatter) || norm_map.has(key))){
			vocab_list.push({word:key, def:definition});
			if (!norm_map.has(key_normalized)){ norm_map.set(key_normalized); } 
		}
    });
    // Sort the array by word 
    vocab_list.sort((a, b) => a.word.toLowerCase().localeCompare(b.word.toLowerCase()));
    // Display the table results
    dv.table(["Word","Definition"],vocab_list.map(b => [b.word, b.def]));
} else {
    dv.paragraph("File not found or has no properties.");
}

It will also turn duplicate keys into lists. For example, it will handle the following case…

fly:: (n) a winged insect
fly:: (v) to move in or pass through the air with wings

I tested it with the following data…

this-is-A-word:: test 2
dog:: a domesticated canid, Canis familiaris, bred in many varieties.
cat:: a small domesticated [carnivore](https://www.dictionary.com/browse/carnivore), Felis domestica or F. catus, bred in a number of varieties.
chicken:: a domestic fowl, Gallus domesticus, descended from various [jungle fowl](https://www.dictionary.com/browse/jungle-fowl), especially the red jungle fowl, and developed in a number of breeds for its flesh, eggs, and feathers.
bread:: a kind of food made of flour or meal that has been mixed with milk or water, made into a dough or batter, with or without yeast or other leavening agent, and baked.
This is a very long key:: Testing
fly:: (n) a winged insect
fly:: (v) to move in or pass through the air with wings
test 1:: aaaa
Test 1:: bbb