Quiero escribir una función que verifique si un objeto tiene al menos un valor que contiene una subcadena. Algo como esto (pseudocódigo):

const userMatchesText = (text, user) => user.includes(text);

La estructura completa de mis objetos (

Entonces, para un usuario como el siguiente:

const user = {
    id: '123abc',
    info: {
        age: 12,
        bio: 'This is my bio' 
    },
    social: {
        chatName: 'Chris',
        friends: ['friend1', 'other friend'],
        blocks: ['Creep']
    }
    //Etc. The objects I'm working with contain nested objects and arrays, etc.
}

, userMatches('bi', user) debería devolver true porque la subcadena 'bi' se encuentra en la biografía: 'esta es mi bi o'. userMatches ('324d, usuario) también debería devolver false. Sin embargo, usermatches('blocks', user) debería devolver false porque la subcadena solo se encuentra en una de las claves, no en uno de los valores.

Los objetos con los que estoy trabajando se ven así (el Esquema de Mangosta):

{
    account  : {
        dateOfCreation : Number
    },
    social   : {
        chatName         : String,
        friends          : [ { type: String } ],
        blocks           : [ { type: String } ],
        sentRequests     : [ { type: String } ],
        recievedRequests : [ { type: String } ],
        threads          : [ { type: String } ]
    },
    info     : {
        age            : Number,
        bio            : String,
        displayName    : String,
        profilePicture : String,
        subjects       : {
            tutor   : [
                {
                    subject : String,
                    level   : String
                }
            ],
            student : [
                {
                    subject : String,
                    level   : String
                }
            ]
        }
    },
    facebook : {
        id         : String,
        firstName  : String,
        middleName : String,
        fullName   : String,
        lastName   : String
    }
}

La mejor manera de hacerlo que he encontrado hasta ahora es la desestructuración de todas las teclas que están fuera del objeto, y luego usar map y includes, como la función a continuación.

const doesUserMatchText = (user, text) => {
    const { social: { chatName }, info: { displayName }, facebook: { firstName, middleName, lastName } } = user;
    const possibleMatches = [ chatName, displayName, firstName, middleName, lastName ];
    let match = false;
    possibleMatches.map(possibleMatch => {
        if (possibleMatch.includes(text)) {
            return (match = true);
        }
    });
};

Sin embargo, esto es realmente molesto (y probablemente también terriblemente ineficiente), ya que los objetos con los que estoy trabajando son realmente grandes. Sería realmente bueno si pudiera llamar userMatchesText(text, user) y obtener un valor booleano. ¡Muchas gracias por adelantado!

Además, tenga en cuenta que no estoy desestructurando todas las teclas que son cadenas. El propósito de esta función es filtrar a los usuarios en función de una consulta de búsqueda, y supuse que tal vez no tenga mucho sentido permitir que los usuarios busquen a otros usuarios por su biografía, identificación, etc., sino solo por sus diversos nombres '.

5
Christoffer Corfield Aakre 3 mar. 2018 a las 00:56

3 respuestas

La mejor respuesta

Puede hacer esto con una función recursiva para atravesar todo el objeto. Solo asegúrese de que el objeto no tenga referencias circulares ...

const user = {
    id: '123abc',
    info: {
        age: 12,
        bio: 'This is my bio' 
    },
    social: {
        chatName: 'Chris',
        friends: ['friend1', 'other friend'],
        blocks: ['Creep']
    }
    //Etc. The objects I'm working with contain nested objects and arrays, etc.
};

function userMatchesText(text, user) {
    if (typeof user === "string") return user.includes(text);
    return Object.values(user).some(val => userMatchesText(text, val));
}

console.log(userMatchesText("bi", user));
console.log(userMatchesText("other fri", user));
console.log(userMatchesText("zzz", user));
4
CRice 2 mar. 2018 a las 22:20

JavaScript puro Esto itera sobre las claves de objeto y tan pronto como encuentra una coincidencia, devuelve verdadero.

El peor de los casos es cuando el resultado es false, itera sobre todas las teclas y subclaves.

(function() {
  var user = {
    id: '123abc',
    info: {
      age: 12,
      bio: 'This is my bio'
    },
    social: {
      chatName: 'Chris',
      friends: ['friend1', 'other friend'],
      blocks: ['Creep']
    }
    //Etc. The objects I'm working with contain nested objects and arrays, etc.
  };

  console.log('userMatches(\'bi\', user): ' + userMatches('bio', user));
  console.log('userMatches(\'324d\', user): ' + userMatches('324d', user));
  console.log('usermatches(\'blocks\', user) ' + userMatches('blocks', user));

  function userMatches(str, obj) {
    var queue = [];
    for (var k in obj) {
      if (obj.hasOwnProperty(k)) {
        if (typeof obj[k] === 'string') {
          if (obj[k].indexOf(str) !== -1) {
            return true;
          }
        } else {
          queue.push(obj[k]);
        }
      }
    }
    if (queue.length) {
      for (var i = 0; i < queue.length; i++) {
        if (userMatches(str, queue[i])) {
          return true;
        }
      }
    }
    return false;
  }
}());
2
lealceldeiro 2 mar. 2018 a las 22:23

Esto debería hacer el truco:

(Ver explicación debajo del código)

const findInObject = (predicate, object) => {
  if (typeof object !== 'object') {
    throw new TypeError('Expected object but got ' + typeof object)
  }
  
  for (let key in object) {
    const value = object[key]
    switch (typeof value) {
      case 'object':
        if (findInObject(predicate, value))
          return true
      default:
        if (predicate(value))
          return true
    }
  }
  return false
}

const userContainsText = (text, user) => 
  findInObject(
    val => {
      if (typeof val !== 'string')
        return false
        
      return val.includes(text)
    },
    user
  )
  
const user = {
    id: '123abc',
    info: {
        age: 12,
        bio: 'This is my bio' 
    },
    social: {
        chatName: 'Chris',
        friends: ['friend1', 'other friend'],
        blocks: ['Creep']
    }
}

console.log(userContainsText('Chris', user))

La función findInObject realiza el trabajo pesado. Proporciona un predicado (que es una función que devuelve true o false en función de si la entrada "pasa") y un objeto para buscar. Ejecuta el predicado en cada clave del objeto, recursivamente si el objeto suministrado contiene objetos. Debería dejar de buscar si obtiene una coincidencia. De lo contrario, viaja todo el objeto.

La función userContainsText usa findInObject. Proporciona un predicado que verifica el contenido de cualquier cadena que obtenga. (Cualquier otro tipo no pasa la prueba). Esta función acepta el texto a buscar y el objeto de usuario a buscar (aunque técnicamente puede hacerlo cualquier objeto, no específicamente un objeto "usuario").

1
Sidney 2 mar. 2018 a las 22:18