arbre-app / read-gedcom

A modern Gedcom parser written in Typescript
https://docs.arbre.app/read-gedcom
MIT License
15 stars 4 forks source link

getGivenName() and getSurname() behave unexpectedly #4

Closed lschierer closed 2 years ago

lschierer commented 2 years ago

I have the following code currently:

const lis = [];
    for (const i of this.Members) {
      const n = (i.getName() as rgc.SelectionNamePieces);
      var f = '';
      var l = '';
      var s = '';

      if(n !== null) {
        if(Array.isArray(n)) {
          n.forEach(v => {
            if(v !== null) {                
              f = v.getGivenName().value();                
              l = v.getSurname().value();
              s = v.getNameSuffix().value();
              if((s !== null) && (s.normalize() === this.SurName.normalize())) {
                lis.push(html`<li>${f} ${l} ${s}</li>`);
              }
            }
          });
        } else if(n instanceof rgc.SelectionNamePieces) {
              f = n.getGivenName().value();
              l = n.getSurname().value();
              s = n.getNameSuffix().value();
          lis.push(html`<li>${f} ${l} ${s}</li>`);
        }
      } else {
        console.log('why is n null?');
      }
    }

for the following gedcom entry

0 @I0002@ INDI
1 NAME Lily J. /Evans/
2 GIVN Lily J.
2 SURN Evans
2 SOUR @S0035@
3 PAGE https://web.archive.org/web/20170630235801/https://www.pottermore.com/explore-the-story/lily-potter
1 NAME Lily /Potter/
2 TYPE married
2 GIVN Lily
2 SURN Potter
2 SOUR @S0002@
3 PAGE 135
1 SEX F
1 BIRT
2 DATE 30 JAN 1960
2 SOUR @S0002@
3 PAGE 135
1 DEAT
2 DATE 31 OCT 1980
2 SOUR @S0002@
3 PAGE 135
1 EDUC Hogwarts
2 DATE CAL BET 1 SEP 1971 AND 30 JUN 1978
1 FAMC @F0059@
2 PEDI birth
1 FAMS @F0000@
1 SOUR @S0035@
2 PAGE https://web.archive.org/web/20170630235801/https://www.pottermore.com/explore-the-story/lily-potter
1 SOUR @S0004@
2 PAGE https://harrypotter.fandom.com/wiki/Lily_J._Potter
1 NOTE @N0002@
1 CHAN
2 DATE 18 OCT 2021
3 TIME 03:36:13

this returns two given names for the one time it matches a this.SurName. It seems to be grabbing every possible GivenName regardless of its relationship to the SurName object that I just compared.

is there no way to iterate through the various names a person might have? the problem with valueAsParts() on names is that its return is so ambiguous, I have no clue if I have a name that has a prefix or a suffix when I get a result back that has three values.

Similarly, I get two Surnames returned even though I only push when

FlorianCassayre commented 2 years ago

I believe the problem comes from how you initialize this.Members. Here is what you may have possibly done:

this.Members = gedcom.getIndividualRecord(); // : SelectionIndividualRecord

In that case you end up with a selection containing all individuals, which means that if you access a property you'll in fact enumerate the properties of all individuals.

What you want instead is an array of selections containing one individual each, and there is a method for that:

this.Members = gedcom.getIndividualRecord().arraySelect(); // : SelectionIndividualRecord[]
lschierer commented 2 years ago

this.Members is created by the following function,

  protected findMembers(target: Element) {
    const g = (target as HPGC).myGedData;
    var m: rgc.SelectionIndividualRecord[] = [];
    if ((typeof(this.SurName) !== 'undefined') && (this.SurName !== '')) {
      console.log('Sur is ' + this.SurName);
      g.getIndividualRecord().arraySelect().forEach(i => {
        const s = (i.getName() as rgc.SelectionNamePieces).getSurname().value();
        if(s !== null) {
          if(Array.isArray(s)) {
            s.forEach(v => {
              if((v !== null) && (v.normalize() === this.SurName.normalize())) {
                m.push(i);
              } else {
              }
            });
          } else if(typeof(s) === 'string') {
            if((s as string).normalize() === this.SurName.normalize()) {
              m.push(i);
            } else {
            }
          } else {
            console.log('entry neither a string nor array');
          }
        }
      });
      console.log('Members at ' + m.length);
    }
    console.log('about to return, m is at ' + m.length);
    return m;
  };

currently the output of the two functions looks like

Harry Potter
James Potter
Lily J.Lily EvansPotter
Fleamont Potter
Euphemia Potter
DoreaDorea BlackPotter
Charlus Potter
Potter
Henry Potter
Hardwin Potter
Potter
Potter

which given the gedcom file I have is correct, except for the two people who married into the family, they show up

<maidenfirstname><marriedfirstname> <maidenlastname><marriedlastname>

which is why I'm looking for a similar arraySelect() function for names of a single SelectionIndividualRecord so that when I call v.getGivenName().value(); I do not return an array of all GivenNames the person has ever had.

FlorianCassayre commented 2 years ago

I understand now, thanks for the clarification. This is a common problem and the recommended solution is to use filterSelect. In your specific case, it should look something along those lines:

const iNamePartsNonMaiden: (null | (undefined | string)[])[] = i
  .getName()
  .filterSelect(nameField =>
    !nameField.getType().value().includes(ValueNameType.Married)
  )
  .valueAsParts();

(don't forget to import ValueNameType)

Note that if you want to convert an array of selections into a single selection, it is also possible thanks to the static method to. It's not needed here, but useful in some cases.

lschierer commented 2 years ago

For the record

0 @I0002@ INDI
1 NAME Lily J. /Evans/
2 GIVN Lily J.
2 SURN Evans
2 SOUR @S0035@
3 PAGE https://web.archive.org/web/20170630235801/https://www.pottermore.com/explore-the-story/lily-potter
1 NAME Lily /Potter/
2 TYPE married
2 GIVN Lily
2 SURN Potter
2 SOUR @S0002@
3 PAGE 135
1 SEX F
1 BIRT
2 DATE 30 JAN 1960
2 SOUR @S0002@
3 PAGE 135
1 DEAT
2 DATE 31 OCT 1980
2 SOUR @S0002@
3 PAGE 135
1 EDUC Hogwarts
2 DATE CAL BET 1 SEP 1971 AND 30 JUN 1978
1 FAMC @F0059@
2 PEDI birth
1 FAMS @F0000@
1 SOUR @S0035@
2 PAGE https://web.archive.org/web/20170630235801/https://www.pottermore.com/explore-the-story/lily-potter
1 SOUR @S0004@
2 PAGE https://harrypotter.fandom.com/wiki/Lily_J._Potter
1 NOTE @N0002@
1 CHAN
2 DATE 22 JUN 2022
3 TIME 13:42:25

the updated function

import * as rgc from 'read-gedcom';

protected render() {
    console.log('start of render');

    const lis = [];
    for (const i of this.Members) {
      const n = i.getName();
      var f = '';
      var l = '';
      var s = '';
      const iNamePartsNonMaiden: (null | (undefined | string)[])[] = i.getName()
        .filterSelect(nameField => nameField.getType() !== rgc.ValueNameType.Married).valueAsParts();

      if( (iNamePartsNonMaiden!== null) && (typeof(iNamePartsNonMaiden) !== 'undefined')) {
        console.log('values are: ' + iNamePartsNonMaiden);
        f = i.getName().filterSelect(nameField => nameField.getType() !== rgc.ValueNameType.Married).getGivenName();
        l = n.getSurname().value();
        s = n.getNameSuffix().value();
        lis.push(html`<li>${f} ${l} ${s}</li>`);
      } else {
        console.log('why is n null?');
      }
    }

    return html`
      <h4 class="h3">${this.SurName}</h4>
      We have ${this.Members.length} members of the ${this.SurName} family.
      <ul>
        ${lis}
      </ul>
    `;
  };

still does the wrong thing. most notably, the console.log statement on iNamePartsNonMaiden prints out

values are: Lily J.,Evans,,Lily,Potter,

if I change the filter select to look for === rgc.ValueNameType.Married just to see what happens, then that console.log shows a bunch of empty values, apparently none of the names are of type Married? Given the entry I have pasted above, I would have guessed my import statement did not work, but npm run build is not complaining.

FlorianCassayre commented 2 years ago

There was a typo in my response, I fixed it quickly but you may have picked the old version. The predicate should be:

nameField => !nameField.getType().value().includes(ValueNameType.Married)

I cannot test it right now, I'm only looking at the documentation but I can check it more thoroughly later.

lschierer commented 2 years ago

Oh, that's perfect, thank you so very much for your time!!