Dataviewjs Code: Searching for Keys with Matching Values in a Key-Value Series

I’m having trouble writing a Dataviewjs code. I have some key-value pairs outside of the Frontmatter, with the format [key:: value]. I need to be able to find all keys that have the same value.

For example, if the input data is the following:

[key1:: 3]
[key2:: 4]
[key3:: 3]
[key4:: 0]
[key5:: 3]

The output should be the following:

key1
key3
key5

I hope you can help me. Tanks!

With a bit help of chatgpt, I managed to get what you’re looking for with dataviewjs:

```dataviewjs
function getKeysWithSameValue(obj) {
  let keysByValue = {};

  for (let key in obj) {
    let value = obj[key];

    if (keysByValue[value]) {
      keysByValue[value].push(key);
    } else {
      keysByValue[value] = [key];
    }
  }

  let identicalKeys = Object.values(keysByValue)
    .filter(keys => keys.length > 1)
    .flat();

  return identicalKeys;
}

let result = getKeysWithSameValue(dv.current());
dv.list(result)
```
2 Likes

(Hi again @Anwen, sorry for nitpicking… :slight_smile: )

I wanted to look into this, and found this solution by @Anwen, and tried it out in the context of what would happen if you have multiple values having more than one key for their value ? This eventually lead to this query:

---
Tags: f75316
---
questionUrl:: http://forum.obsidian.md/t//75316

[key1:: 3]
[key2:: 4]
[key3:: 3]
[key4:: 0]
[key5:: 3]
[key6:: 4]

```dataviewjs
function getKeysWithSameValue(obj) {
  let keysByValue = {};

  for (let key in obj) {
    let value = obj[key];

    if (keysByValue[value]) {
      keysByValue[value].push(key);
    } else {
      keysByValue[value] = [key];
    }
  }
  
  let identicalKeys = Object.entries(keysByValue)
    .filter(f => f[1].length > 1 )
    .map(m => new ListPairWidget(m[0], m[1]) );

  return identicalKeys;
}

let result = getKeysWithSameValue(dv.current());
dv.list(result)
```

Which now outputs the combination of value and the keys with that value. If I ran this query with the original query, all the keys in the sublists was joined together in a single list.

In this query I extend the logic of building identicalKeys to include the value as well as keeping the identical keys as lists without flattening them. To achieve the double output list, I use the trick of creating a object of the type ListPairWidget() which I found through inspection of the source of dataview.

The output of this query (which kind of took me by a surprise firstly):

Some comments on this output:

  • I expected the two first since I added key6:: 4 to get another set of unique values with different keys
  • The surprise, at first, was related to how dataview sanitises key values causing them to appear twice one with the case preserved, and one with the key sanitised (that is lowercase and space turned into dashes). This was applied to both Tags vs tags, and questionUrl vs questionurl

One slightly hackish variant to removing those sanitised variants:

```dataviewjs
function getKeysWithSameValue(obj) {
  let keysByValue = {};

  for (const [key, value] of Object.entries(obj) ) {
  
    if (keysByValue[value]) {
      keysByValue[value].push(key);
    } else {
      keysByValue[value] = [key];
    }
  }
  
  let oneValueMultipleKeysList = Object.entries(keysByValue)
    .filter(f => f[1].length > 1 )
    .filter(f => f[1].length != 2 ||
      f[1][0].toString().toLowerCase().replace(" ", "-") !=
      f[1][1].toString().toLowerCase().replace(" ", "-") )
    .map(m => new ListPairWidget(m[0], m[1].join(", ")) );

  return oneValueMultipleKeysList;
}

let result = getKeysWithSameValue(dv.current());
dv.list(result)
```

This an additional filter where the only values being let through are those with more than two keys, or if two keys having different keys after being sanitised. Now we get (with joined keys) the expected output of:
image

This can of course also be altered to only output the separate sets of keys having the same value if the value is uninteresting, or other variations as you see fit.

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.