Unique values from 2 differnet lists

HI All,

if i have 2 lists stored in variables , so Var1 would have [ a ,b ,c ] and Var2 has [a,b,c,d]

how can i compare them against each other , take out the duplicates then leave the unique value which is “d”

[enlist<var1>] :intersection[enlist<var2>]

1 Like

That gives the intersection, but it looks like the OP wants those unique to either of the lists.

So perhaps

[enlist<var1>] [enlist<var2>] -[enlist<var1>] :intersection[enlist<var2>]

Edit: no, that doesn’t do it. It just happens to work on these inputs. Let me think a bit more.

1 Like

You are right, I misread the request. You could save the intersection as a variable and reference it in an :except filter run.

Something along these lines:

<$let 
	intersection="[enlist<var1>] :intersection[enlist<var2>]"
	unique={{{ [enlist<var1>] [enlist<var2>] :except[subfilter<intersection>] :and[format:titlelist[]join[ ]]}}}
>
	
	
</$let>
1 Like

Is there something still not working here, or am I using it incorrectly?:

<$let
  var1="a b c d"
  var2="a b c e"
>
<$let 
	intersection="[enlist<var1>] :intersection[enlist<var2>]"
	unique={{{ [enlist<var1>] [enlist<var2>] :except[subfilter<intersection>]}}}
>

* var1: <<var1>>
* var2: <<var2>>
* intersection: <<intersection>>
* unique: <<unique>>

</$let>
</$let>

yields:

* var1: a b c d
* var2: a b c e
* intersection: [enlist] :intersection[enlist]
* unique: d

I’m expecting unique to include 'd' and 'e' in either order. I was trying this other ways after my false start and I just kept coming up with something wrong. Am I using it wrong?

(I’m not particularly worried about how intersection displays. I know that the generated HTML will actually look like intersection: [enlist<var1>] :intersection[enlist<var2>] and that the DOM will then have extra nodes in it. But why does unique just have the one item?)

1 Like

I have corrected the second filter in my previous post, which was returning just the first value and not all of them. Filtered attributes use just the first title from the result of evaluating the filter.

1 Like

That fixes it for me. But man I really want some way to more simply express that

symmetric-difference = union - intersection.

I’m not the OP, but I want to say thank you once again for the effort you put into teaching people here!

Yes I can relate to that feeling, in particular with longer lists the solution I provided would not be very efficient.

Thank you very much

sorry if its a stupid question , but what does the last be does ? :and[format:titlelist[]join[ ]

format:titlelist[] adds doubled square brackets around any list item that contains spaces. Then, join[ ] combines all items into a single list of space-separated items so that the entire list can be assigned to the unique variable using the $let widget.

1 Like

Just an aside, if you knew the first list was part of the second but something was added, a more specific case of the OT then you could do the following; (see below for a general solution)

  • These are somewhat “intellectual” examples but illustrate other approaches depending on the qualities of the input.
  • @paulgilbert2000 ignore the following unless you want to explore this further
  • I felt obliged to flag alternatives and stimulate conversation with this specific case.
\define var1() a b c
\define var2() a b c d e

1: <<var1>>, 2: <<var2>>

{{{ [subfilter<var1>] [subfilter<var2>] -[subfilter<var1>]}}}
  • First list plus second list is automatically deduplicated
  • remove all items from the first list.
  • In this example you get two “unique values” d and e.
  • It also illustrates how the subfilter can act as a enlist or split[ ] a string into one or more titles.

This is not a general solution unless the input data has a particular quality, but there may be cases where this is so, for example comparing one list after adding something to it.

Using the same assumptions in 5.3.0 we can do this

\function .1st() a b c
\function .2nd() a b c d

{{{ [.2nd[]!.1st[]] }}}

I suspect we can use these mechanisiums to do this all another way.

If you were interested in all the possible intersections this method can be extended, and whilst I use macros perhaps you could put it all in a let widget (below)

  • This and the let method do find the real intersection listed as all unique
\define var1() a b c z y
\define var2() a b c d e y
\define full() [subfilter<var1>] [subfilter<var2>]
\define not-in-1() [subfilter<full>] -[subfilter<var1>]
\define not-in-2() [subfilter<full>] -[subfilter<var2>]
\define all-unique() [subfilter<not-in-1>] [subfilter<not-in-2>]

* Full: {{{ [subfilter<full>] }}}
* not-in-1: {{{ [subfilter<not-in-1>] }}}
* not-in-2: {{{ [subfilter<not-in-2>] }}}
* All unique {{{ [subfilter<all-unique>] }}}

With a let widget;

<$let 
var1="a b c z y"
var2="a b c d e y"
full="[subfilter<var1>] [subfilter<var2>]"
not-in-1="[subfilter<full>] -[subfilter<var1>]"
not-in-2="[subfilter<full>] -[subfilter<var2>]"
all-unique="[subfilter<not-in-1>] [subfilter<not-in-2>]"
>

* Full: {{{ [subfilter<full>] }}}
* not-in-1: {{{ [subfilter<not-in-1>] }}}
* not-in-2: {{{ [subfilter<not-in-2>] }}}
* All unique {{{ [subfilter<all-unique>] }}}
</$let>

Prior to 5.3.0 being in prerelease, I actualy cloned the subfilter operator and named it ƒ because I thought that would be a lot nicer, for example;

<$let 
var1="a b c z y"
var2="a b c d e y"
full="[ƒ<var1>] [ƒ<var2>]"
not-in-1="[ƒ<full>] -[ƒ<var1>]"
not-in-2="[ƒ<full>] -[ƒ<var2>]"
all-unique="[ƒ<not-in-1>] [ƒ<not-in-2>]"
>

* Full: {{{ [ƒ<full>] }}}
* not-in-1: {{{ [ƒ<not-in-1>] }}}
* not-in-2: {{{ [ƒ<not-in-2>] }}}
* All unique {{{ [ƒ<all-unique>] }}}
</$let>
  • This is in part because subfilter is not only sub, it can be a standalone filter run.
  • note not a single split, format:titlecase[] or enlist/enlist-input anywhere.
  • There are other possibilities in 5.3.0+
1 Like

Thank you tones , this was very helpful too!!

Just to offer a different angle: I’m not getting the following two filter run expressions to merge well into a single filter — my variations with :or, and variations with :all keep getting the scope wrong (and I need to turn to other work) — but the following logic would be more intuitive, right?

{{{ [enlist<Var2>] -[enlist<Var1>] }}}
{{{ [enlist<Var1>] -[enlist<Var2>] }}}

Isn’t there some way to get the union of these two filter runs?

This isn’t exactly what you’re looking for, @Springer—I’m not sure how to do that in a single filter without defining subfilters—but here’s an alternate approach I came across while poking at it:

<$let 
var1="a b c z y"
var2="a b c d e y"
full="[enlist<var1>] =[enlist<var2>]"
>

Unique values: {{{ [subfilter<full>unique[]] :filter[subfilter<full>match<currentTiddler>count[]match[1]] }}}

</$let>

I’ve no idea how this performs relative to @saqimtiaz’s solution, though… probably not very well.

1 Like