//return an array of objects according to key, value, or key and value matching
function getObjects(obj, sKey, val) {
  var arObjects = [];
  for (var i in obj) {
    if (!obj.hasOwnProperty(i)) continue;
    if (typeof obj[i] === 'object') {
      arObjects = arObjects.concat(getObjects(obj[i], sKey, val));
    } else {
      //if key matches and value matches or if key matches and value is not passed (eliminating the case where key matches but passed value does not)
      if ((i === sKey) && (obj[i] === val) || (i == sKey) && (val == '')) { //
        arObjects.push(obj);
      } else if (obj[i] == val && sKey == '') {
        //only add if the object is not already in the array
        if (arObjects.lastIndexOf(obj) == -1) {
          arObjects.push(obj);
        }
      }
    }
  }
  return arObjects;
}

//return an array of values that match on a certain key
function getValues(obj, key) {
  var objects = [];
  for (var i in obj) {
    if (!obj.hasOwnProperty(i)) continue;
    if (typeof obj[i] === 'object') {
      objects = objects.concat(getValues(obj[i], key));
    } else if (i === key) {
      objects.push(obj[i]);
    }
  }
  return objects;
}

/**
 * return an array of keys that match on a certain value
 * @param {json} jData json to serach in
 * @param {string} sVal string which is searched
 * @returns {string array} with all keys containing val
 */
function getKeysOrg(jData, sVal) {
  let objects = [];
  for (let i in jData) {
    if (!jData.hasOwnProperty(i)) continue;
    if (typeof jData[i] === 'object') {
      objects = objects.concat(getKeys(jData[i], sVal));
    } else if (jData[i] === sVal) {
      objects.push(i);
    }
  }
  return objects;
}

/**
 * return an array of keys (with path) that match on a certain value
 * @param {json} jData json to serach in
 * @param {string} sVal string which is searched
 * @param {string} sPath path string, "" at root level
 * @param {boolean} bIncludes false:value must be identical true:sVal must be included in value
 * @returns {string array} with all keys containing val
 */
function getKeys(jData, sVal, sPath, bIncludes) {
  let objects = [];
  let sPath2 = sPath
  for (let i in jData) {
    if (sPath2 === "")
      sPath2 = i
    else
      sPath2 = sPath + "." + i
    if (!jData.hasOwnProperty(i)) continue;
    if (typeof jData[i] === 'object') {
      objects = objects.concat(getKeys(jData[i], sVal, sPath2, bIncludes));
    } else {
      if (bIncludes === true) {
        // value of item contains sVal
        let sCmp = jData[i] + ""
        if (sCmp.includes(sVal)) {
          let regEx = new RegExp(`(${sVal})`, "g")
          sPath = sPath + "." + i
          objects.push(i + FS_FOUND + sCmp.replace(regEx, '<z>$1</z>'))
        }
      } else { // value of item is identical to sVal
          if (jData[i] === sVal) {
            sPath = sPath + "." + i
            objects.push(i + FS_FOUND + jData[i])
          }
        }
    }
  }
  return objects;
}
/**
 * return an array of keys (with path) that match on a certain value
 * @param {json} jData json to serach in
 * @param {string} sVal string which is searched
 * @param {string} sPath path string, "" at root level
 * @returns {string array} with all keys containing val
 */
function getKeys2(jData, sVal, sPath) {
  let objects = [];
  let sPath2 = sPath
  for (let i in jData) {
    if (sPath2 === "")
      sPath2 = i
    else
      sPath2 = sPath + "." + i
    if (!jData.hasOwnProperty(i)) continue;
    if (typeof jData[i] === 'object') {
      objects = objects.concat(getKeys2(jData[i], sVal, sPath2));
    } else {
      let sText = "" + jData[i]
      if (sText.startsWith(sVal)) {
        sPath = sPath + "." + i
        objects.push(i);
      }
    }
  }
  return objects;
}