如何从javascript对象的键和值获取路径

How to get the path from javascript object from key and value

本文关键字:键和值 获取 路径 对象 javascript      更新时间:2023-09-26

我有一个javascript对象宽度深度。

我需要知道这个键在对象中的确切路径:"obj1.obj2.data1"

我已经知道键是data1,值是123。

我的javascript对象是这样的

{
    obj1: {
        obj2: {
            data1: 213,
            data2: "1231",
            obj3: {
                data: "milf"
            }
        }
    },
    obj4: {
        description: "toto"
    }
}

我怎么才能做到呢?

这里是一个jsfiddle: http://jsfiddle.net/3hvav8xf/8/我试图实现getPath。

我认为递归函数可以帮助你(更新版本,检查值)

function path(c, name, v, currentPath, t){
    var currentPath = currentPath || "root";
    for(var i in c){
      if(i == name && c[i] == v){
        t = currentPath;
      }
      else if(typeof c[i] == "object"){
        return path(c[i], name, v, currentPath + "." + i);
      }
    }
    return t + "." + name;
};
console.log(path({1: 2, s: 5, 2: {3: {2: {s: 1, p: 2}}}}, "s", 1));

下面的命令在任何级别的嵌套对象中查找路径。还有数组。它返回找到的所有路径,如果您有相同名称的键,这是您想要的。

我喜欢这种方法,因为它适用于lodash方法getset开箱即用。

function findPathsToKey(options) {
  let results = [];
  (function findKey({
    key,
    obj,
    pathToKey,
  }) {
    const oldPath = `${pathToKey ? pathToKey + "." : ""}`;
    if (obj.hasOwnProperty(key)) {
      results.push(`${oldPath}${key}`);
      return;
    }
    if (obj !== null && typeof obj === "object" && !Array.isArray(obj)) {
      for (const k in obj) {
        if (obj.hasOwnProperty(k)) {
          if (Array.isArray(obj[k])) {
            for (let j = 0; j < obj[k].length; j++) {
              findKey({
                obj: obj[k][j],
                key,
                pathToKey: `${oldPath}${k}[${j}]`,
              });
            }
          }
          if (obj[k] !== null && typeof obj[k] === "object") {
            findKey({
              obj: obj[k],
              key,
              pathToKey: `${oldPath}${k}`,
            });
          }
        }
      }
    }
  })(options);
  return results;
}
findPathsToKey({ obj: objWithDuplicates, key: "d" })
// ["parentKey.arr[0].c.d", "parentKey.arr[1].c.d", "parentKey.arr[2].c.d"]

试试这里- https://jsfiddle.net/spuhb8v7/1/

如果您希望结果是单个键(第一次遇到),您可以将results更改为字符串,如果定义了,然后返回函数。

我最终得到了以下函数,它适用于嵌套的对象/数组:

function findPath (obj, name, val, currentPath) {
  currentPath = currentPath || ''
  let matchingPath
  if (!obj || typeof obj !== 'object') return
  if (obj[name] === val) return `${currentPath}['${name}']`
  for (const key of Object.keys(obj)) {
    if (key === name && obj[key] === val) {
      matchingPath = currentPath
    } else {
      matchingPath = findPath(obj[key], name, val, `${currentPath}['${key}']`)
    }
    if (matchingPath) break
  }
  return matchingPath
}
const treeData = [{
  id: 1,
  children: [{
    id: 2
  }]
}, {
  id: 3,
  children: [{
    id: 4,
    children: [{
      id: 5
    }]
  }]
}]
console.log(findPath (treeData, 'id', 5))

给你!

function getPath(obj, value, path) {
    if(typeof obj !== 'object') {
        return;
    }
    for(var key in obj) {
        if(obj.hasOwnProperty(key)) {
            console.log(key);
            var t = path;
            var v = obj[key];
            if(!path) {
                path = key;
            }
            else {
                path = path + '.' + key;
            }
            if(v === value) {
                return path;
            }
            else if(typeof v !== 'object'){
                path = t;
            }
            var res = getPath(v, value, path);
            if(res) {
                return res;
            } 
        }
    }
}
getPath(yourObject, valueYouWantToFindPath);

如果找到则返回路径,否则返回未定义。我只是用实物测试过它。比较非常严格(即:使用===)。

更新:以key作为参数的更新版本。

function getPath(obj, key, value, path) {
    if(typeof obj !== 'object') {
        return;
    }
    for(var k in obj) {
        if(obj.hasOwnProperty(k)) {
            console.log(k);
            var t = path;
            var v = obj[k];
            if(!path) {
                path = k;
            }
            else {
                path = path + '.' + k;
            }
            if(v === value) {
                if(key === k) {
                    return path;
                }
                else {
                    path = t;
                }
            }
            else if(typeof v !== 'object'){
                path = t;
            }
            var res = getPath(v, key, value, path);
            if(res) {
                return res;
            } 
        }
    }
}
getPath(yourObject, key, valueYouWantToFindPath);

JSON Object可以在JavaScript中作为关联数组处理。

所以你可以循环遍历和存储"parent "的索引

假设整个对象存储在名为obj的变量中。

for(var p1 in obj)

{

   for( var p2 in obj[ p1 ] )
   {
       for( var p3 in obj[ p1 ][ p2 ] )
       {
           // obj[ p1 ][ p2 ][ p3 ] is current node
           // so for Your example it is obj.obj1.obj2.data1
       }
   }

}

希望回答是有帮助的。

我将按如下方法做这项工作;

Object.prototype.paths = function(root = [], result = {}) {
  var ok = Object.keys(this);
  return ok.reduce((res,key) => { var path = root.concat(key);
                                  typeof this[key] === "object" &&
                                         this[key] !== null ? this[key].paths(path,res)
                                                            : res[this[key]] == 0 || res[this[key]] ? res[this[key]].push(path)
                                                                                                    : res[this[key]] = [path];
                                  return res;
                                },result);
};
var myObj = {
    obj1: {
        obj2: {
            data1: 213,
            data2: "1231",
            obj3: {
                data: "milf"
            }
        }
    },
    obj4: {
        description: "toto",
        cougars: "Jodi",
        category: "milf"
    }
},
value = "milf",
milfPath = myObj.paths()[value]; // the value can be set dynamically and if exists it's path will be listed.
console.log(milfPath);

几句警告:我们在使用Object原型时应该谨慎。我们的修改应该有描述符enumerable = false,否则它将在for in循环中列出,例如jQuery将无法工作。(这是多么愚蠢的jQuery,因为显然他们没有使hasOwnProperty检查在他们的for in循环)一些好的读取在这里和这里,所以我们必须添加这个对象方法与Object.defineProperty(),使其成为enumerable = false;。但为了简单起见,并保持在问题的范围内,我没有在代码中包含这一部分。

这是我写的一个相当短的,相对容易理解的函数,用于检索对象上每个属性/字段的JSON路径(无论嵌套有多深)。

getPaths(object)函数只是接受你想要JSON路径的对象,并返回一个路径数组。或者,如果您希望初始对象用与标准JSON路径符号$不同的符号表示,您可以调用getPaths(object, path),每个JSON路径将以指定的路径开始。

例如:getPaths({prop: "string"}, 'obj');将返回以下JSON路径:obj.prop,而不是$.prop

请参阅下面的getPaths返回的更详细、更深入的示例,以及如何使用它。

object = {
  "firstName": "John",
  "lastName": "doe",
  "age": 26,
  "fakeData": true,
  "address": {
    "streetAddress": "fake street",
    "city": "fake city",
    "postalCode": "12345"
  },
  "phoneNumbers": [{
    "type": "iPhone",
    "number": "0123-4567-8888"
  }, {
    "type": "home",
    "number": "0123-4567-8910"
  }]
};
function getPaths(object, path = "$") {
  return Object.entries(object).flatMap(function(o, i) {
    if (typeof o[1] === "object" && !o[1].length) {
      return `${getPaths(o[1], path + '.' + o[0])}`.split(',');
    } else if (typeof o[1] === "object" && o[1].length) {
      return Object.entries(o[1]).flatMap((no, i) => getPaths(no[1], `${path}.${o[0]}[${i}]`));
    } else {
      return `${path}.${o[0]}`;
    }
  });
}
console.log(`%o`, getPaths(object));

我真的很喜欢Roland Jegorov的答案,但我有一个非常复杂的对象,我需要搜索,这个答案不能解释它。

如果你是在像我这样的情况下,你可能想要首先确保你没有循环引用(否则你会遇到无限搜索)。有几种方法可以做到这一点,但我不得不将我的对象字符串化以将其复制到其他窗口,所以我最终使用了这个循环替换器:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value

(更新在这里-我做了一个小的改变从MDN的getCircularReplacer函数,所以它不再离开函数引用,因为这是我正在寻找的!)

(更新3 -我还想检查类的任何实例的方法,但我只是返回"函数"太早,所以我已经调整它包括实例方法。我想它终于像我想的那样工作了!)

const getCircularReplacer = () => {
  const seen = new WeakSet();
  return (key, value) => {
    if (typeof value === "function") {
      if (value?.prototype) {
        if (seen.has(value.prototype)) {
          return;
        }
        seen.add(value.prototype)
        return value.prototype
      }
      return "function";
    }
    if (typeof value === "object" && value !== null) {
      if (seen.has(value)) {
        return;
      }
      seen.add(value);
    }
    return value;
  };
};
const nonCyclicObject = JSON.parse(JSON.stringify(myComplexObject, getCircularReplacer()));

然后我使用了Roland的这个修改版本的答案:

(更新2:我必须确保在找到键后不返回,因为如果对象的第一层具有该键,则它总是在只调用函数一次后返回)

function findPathsToKey(options) {
  let count = 0;
  let results = [];
  (function findKey({
    key,
    obj,
    pathToKey,
  }) {
    count += 1;
    if (obj === null) return;
    
    const oldPath = `${pathToKey ? pathToKey + "." : ""}`;
    if (Object.hasOwnProperty.call(obj, key)) {
      results.push(`${oldPath}${key}`);
    }
    
    if (typeof obj === "object" && !Array.isArray(obj)) {
      for (const k in obj) {
        if (Object.hasOwnProperty.call(obj, k)) {
          if (Array.isArray(obj[k])) {
            for (let j = 0; j < obj[k].length; j++) {
              findKey({
                obj: obj[k][j],
                key,
                pathToKey: `${oldPath}${k}[${j}]`,
              });
            }
          }
          if (typeof obj[k] === "object") {
            findKey({
              obj: obj[k],
              key,
              pathToKey: `${oldPath}${k}`,
            });
          }
        }
      }
    }
  })(options);
  return { count, results };
};

计数只是为了解决一些问题,并确保它实际上运行了我认为的键数。希望这有助于其他人寻找解决方案!

⚠️这段代码没有回答这个问题,但做了相关的工作:将嵌套对象转换为使用dot.divided.path作为键和非对象值的查询对象;与URlSearchParams &qs。也许会对某人有用。

const isPlainObject = (v) => {
  if (Object.prototype.toString.call(v) !== '[object Object]') return false;
  const prototype = Object.getPrototypeOf(v);
  return prototype === null || prototype === Object.prototype;
};
const objectToQueryObject = (obj, path) => {
  return Object.entries(obj).reduce((acc, [key, value]) => {
    const newPath = path ? `${path}.${key}` : key;
    if (isPlainObject(value)) {
      return {
        ...acc,
        ...objectToQueryObject(value, newPath)
      };
    }
    acc[newPath] = value;
    return acc;
  }, {})
};
const queryObjectRaw = {
  value: {
    field: {
      array: {
        '[*]': {
          field2: {
            eq: 'foo',
            ne: 'bar',
          }
        }
      },
      someOtherProp: { in: [1, 2, 3],
        ne: 'baz',
      }
    },
    someOtherField: {
      gt: 123
    },
  },
  otherValue: {
    eq: 2
  },
};
const result = objectToQueryObject(queryObjectRaw);
console.log('result', result);
const queryString = new URLSearchParams(result).toString();
console.log('queryString', queryString);

如果您只知道值而不知道键,并且希望查找包含该值的所有路径,则使用此方法。
它将找到所有具有该值的属性,并打印每个创建值的完整路径。

const createArrayOfKeys = (obj, value) => {
    const result = []
    function iter(o) {
      Object.keys(o).forEach(function(k) {
        if (o[k] !== null && typeof o[k] === 'object') {
          iter(o[k])
          return
        }
        if (o[k]=== value) {
          
          
          result.push(k)
          return
          }
      })
    }
    iter(obj)
    return result
  }
function findPath (obj, name, val, currentPath) {
  currentPath = currentPath || ''
  let matchingPath
  if (!obj || typeof obj !== 'object') return
  if (obj[name] === val) return `${currentPath}/${name}/${val}`
  for (const key of Object.keys(obj)) {
    if (key === name && obj[key] === val) {
      matchingPath = currentPath
    } else {
      matchingPath = findPath(obj[key], name, val, `${currentPath}/${key}`)
    }
    if (matchingPath) break
  }
  return matchingPath
}
const searchMultiplePaths = (obj, value) => {
    const keys = createArrayOfKeys(obj, value)
    console.log(keys);
    keys.forEach(key => {
        console.log(findPath(obj, key, value))
    })
}
var data = { ffs: false, customer: { customer_id: 1544248, z_cx_id: '123456' }, selected_items: { '3600196': [{ id: 4122652, name: 'Essential Large (up to 8''x10'')', selected: true }] }, service_partner: { id: 3486, name: 'Some String', street: '1234 King St.',  hop: '123456' }, subject: 'Project-2810191 - Orange Juice Stain (Rug)', description: 'Product Type: 'n'nIssue: (copy/paste service request details here)'n'nAction Required:', yes: '123456' };
searchMultiplePaths(data, '123456')

我知道这篇文章很老了,但是上面的答案并不能让我真正满意。

一个简单的解决方案是为结构中的每个对象添加对象路径。当你需要的时候,你可以很容易地读取路径。

let myObject = {
    name: 'abc',
    arrayWithObject: [
        {
            name: "def"
        },
        {
            name: "ghi",
            obj: {
                name: "jkl"
            }
        }
    ],
    array: [15, 'mno'],
    arrayArrayObject: [
        [
            {
                name: '...'
            }
        ]
    ]
}
function addPath(obj, path = [], objectPathKey = '_path') {
    if (Array.isArray(obj)) {
        
        obj.map((item, idx) => addPath(item, [...path, idx]))
        
    } else if (typeof obj === "object") {
        
        obj[objectPathKey] = path;
        
        for (const key in obj) {
            obj[key] = addPath(obj[key], [...path, key])
        }
        
    }
    
    return obj
}
myObject = addPath(myObject);
let changeMe = _.cloneDeep(myObject.arrayWithObject[0])
changeMe.newProp = "NEW"
changeMe.newNested = {name: "new", deeper: {name: "asdasda"}}
changeMe = addPath(changeMe, changeMe._path)
_.set(myObject, changeMe._path, changeMe);

更新完成后,清理对象并删除_path属性。

方案优点:

  • 你只做一次工作
  • 你保持你的代码简单
  • 不需要自己的属性检查
  • 无认知超载

我强烈建议您使用lodash来解决这个问题。

在他们的文档中,这应该可以帮助你。

// using "_.where" callback shorthand
_.find(characters, { 'age': 1 });
// →  { 'name': 'pebbles', 'age': 1, 'blocked': false }