How to test if variable exists inside in an object within an array within an object within an array

Sorry about the tongue twister title. I have this array:

const languages = [
  {
    title: '1970s',
    languages: [
      {
        name: 'C',
        year: 1972
      }
    ]
  },
  {
    title: '1980s',
    languages: [
      {
        name: 'C++',
        year: 1983
      },
      {
        name: 'Perl',
        year: 1987
      }
    ]
  },
  {
    title: '1990s',
    languages: [
      {
        name: 'Haskell',
        year: 1990
      },
      {
        name: 'Python',
        year: 1991
      },
      {
        name: 'Java',
        year: 1995
      },
      {
        name: 'Javascript',
        year: 1995
      },
      {
        name: 'PHP',
        year: 1995
      },
      {
        name: 'Ruby',
        year: 1995
      }
    ]
  },
  {
    title: '2000s',
    languages: [
      {
        name: 'C#',
        year: 2000
      },
      {
        name: 'Scala',
        year: 2003
      },
      {
        name: 'Clojure',
        year: 2007
      },
      {
        name: 'Go',
        year: 2009
      }
    ]
  },
  {
    title: '2010s',
    languages: [
      {
        name: 'Elm',
        year: 2012
      }
    ]
  }
];

When the user changes the value of a text input, I want to check if the new value (which I’m saving as var newValue) matches one of the name properties in the above, but the nested nature of the array is causing me headaches. Here’s what I’ve tried:

var result = -1;

function hasName(prop, value, languages) {
    languages.forEach(function(obj, index) {
        prop.forEach(function(obj, index) {
        if (prop in obj && obj[prop] === value) {
            result = index;
            return false;
        }
    	});
    });
    return result;
}

if (result != -1) {
  alert('HALLO!');
}

prop.forEach is throwing an error. The if statement might do as well, although I can’t test that until I figure out what I should be doing instead of prop.forEach. Would really appreciate a poke in the right direction.

Thanks

Hi @MonsieurRenard, how are you actually calling hasName()? You’re trying to iterate over prop, but later you are trying to access obj[prop], which suggests that prop is not supposed to be an array but a string. If you want to test if languages contains an item with item[prop] === value, that could be done like so:

function hasValue(languages, prop, value) {
  return languages.some(function (language) {
    // language is e.g.
    // {
    //   title: '1980s',
    //   languages: [
    //     {
    //       name: 'C++',
    //       year: 1983
    //     },
    //     {
    //       name: 'Perl',
    //       year: 1987
    //     }
    //   ]
    // }
    return language.languages.some(function (item) {
      // item is e.g.
      // {
      //   name: 'C++',
      //   year: 1983
      // },
      return item[prop] === value
    })
  })
}

if (hasValue(languages, 'name', 'C++')) {
  console.log('Hello!')
}

(Also note that returning from .forEach() doesn’t have any effect; use .some() to test if an element in the array passes the test, which will then break from the loop.)

1 Like

how are you actually calling hasName()

Ha, good point. Actually I was trying to clean up the code as much as possible before posting it here - and in refactoring it seems I actually took the call out. Originally it was called like this:

if (hasName('name', newValue, languages) != -1) {
  alert('HALLO!');
}

newValue is a var that reflects the value of the user-edited text-input.

Anyway, having tried the code you suggest it works, thanks a lot. Using arrays is definitely a weak point of mine. Is there any specific reading or watching (on Sitepoint or otherwise) you recommend to help get my head around it?

Following up on this, I’m trying to now figure out how I would get the corresponding title for the correct name. So continuing your example, if the name I was looking for was C++, the title I’m looking for is 1980s - how could I put the correct title in var decade?

Thanks again so much, this is really helpful. Feel like I’m making progress with dealing with arrays!

1 Like

forEach item in languages
if some element of item languages has name == target
return item.title
end forEach
return “No Match”

Glad I could help. :-) You can use .find() then instead of .some() that not only tests if an element passes the predicate function, but also returns that element:

function getTitle(languages, prop, value) {
  // Find the language...
  var result = languages.find(function (language) {
    // ... where some item has item[prop] === value
    return language.languages.some(function (item) {
      return item[prop] === value
    })
  })

  return result ? result.title : null
}

console.log(getTitle(languages, 'name', 'C++')) // "1980s"
console.log(getTitle(languages, 'name', 'C--')) // null

OTTOMH a book I would generally recommend is The Modern Javascript Tutorial that also has an overview about array methods here. For some more advanced techniques you might check out this chapter in Kyle Simpson’s great Functional-Light JavaScript.

1 Like

Fantastic, thanks so much. That code worked perfectly too, thank you!

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