Dado um objeto JS
var obj = { a: { b: '1', c: '2' } }
e uma corda
"a.b"
como posso converter a string em notação de ponto para que eu possa ir
var val = obj.a.b
Se a string fosse justa 'a'
, eu poderia usar obj[a]
. Mas isso é mais complexo. Imagino que exista algum método direto, mas ele escapa no momento.
javascript
nevf
fonte
fonte
eval
é mau; não usá-loRespostas:
Aqui está uma linha única elegante que é 10x mais curta que as outras soluções:
[editar] Ou no ECMAScript 6:
(Não que eu ache o eval sempre ruim, como outros sugerem), mas essas pessoas ficarão satisfeitas com o fato de esse método não usar o eval. O item acima encontrará
obj.a.b.etc
dadosobj
e a string"a.b.etc"
.)Em resposta àqueles que ainda têm medo de usar,
reduce
apesar de estar no padrão ECMA-262 (5ª edição), aqui está uma implementação recursiva de duas linhas:Dependendo das otimizações que o compilador JS está executando, convém garantir que as funções aninhadas não sejam redefinidas em todas as chamadas pelos métodos usuais (colocando-as em um fechamento, objeto ou espaço de nome global).
editar :
Para responder a uma pergunta interessante nos comentários:
(nota de rodapé: infelizmente não é possível retornar um objeto com um setter, pois isso violaria a convenção de chamada; o comentarista parece estar se referindo a uma função geral do estilo setter com efeitos colaterais como
index(obj,"a.b.etc", value)
fazerobj.a.b.etc = value
.)O
reduce
estilo não é realmente adequado para isso, mas podemos modificar a implementação recursiva:Demo:
... embora pessoalmente eu recomendo fazer uma função separada
setIndex(...)
. Gostaria de terminar com uma nota lateral que o poser original da pergunta poderia (deveria?) Estar trabalhando com matrizes de índices (dos quais eles podem obter.split
), em vez de seqüências de caracteres; embora geralmente não haja nada de errado com uma função de conveniência.Um comentarista perguntou:
Javascript é uma linguagem muito estranha; em geral, os objetos só podem ter cadeias de caracteres como suas chaves de propriedade, por exemplo, se
x
fosse um objeto genérico comox={}
, entãox[1]
se tornariax["1"]
... você leu certo ... sim ...Matrizes Javascript (que são instâncias do Object) especificamente incentivam chaves inteiras, mesmo que você possa fazer algo assim
x=[]; x["puppy"]=5;
.Mas em geral (e há exceções),
x["somestring"]===x.somestring
(quando é permitido; você não pode fazerx.123
).(Lembre-se de que qualquer compilador JS que você estiver usando possa escolher, talvez, compilá-los em representações mais saudáveis, se puder provar que não violaria as especificações.)
Portanto, a resposta para sua pergunta depende se você está assumindo que esses objetos aceitam apenas números inteiros (devido a uma restrição no domínio do problema) ou não. Vamos supor que não. Então uma expressão válida é uma concatenação de um identificador de base mais alguns
.identifier
s mais alguns["stringindex"]
sIsso seria equivalente a
a["b"][4]["c"]["d"][1][2][3]
, embora provavelmente devêssemos também apoiara.b["c\"validjsstringliteral"][3]
. Você precisaria verificar a seção de gramática de ecmascript em literais de string para ver como analisar um literal de string válido. Tecnicamente, você também gostaria de verificar (ao contrário da minha primeira resposta) quea
é um identificador javascript válido .Uma resposta simples para sua pergunta, porém, se suas seqüências de caracteres não contiverem vírgulas ou colchetes , seria apenas corresponder ao comprimento 1 ou mais seqüências de caracteres que não estão no conjunto
,
ou[
ou]
:Se suas strings não contiverem caracteres de escape ou
"
caracteres , e porque IdentifierNames são uma sub-linguagem de StringLiterals (acho ???), você pode primeiro converter seus pontos em []:Obviamente, sempre tenha cuidado e nunca confie em seus dados. Algumas maneiras ruins de fazer isso que podem funcionar em alguns casos de uso também incluem:
Edição especial de 2018:
Vamos para um círculo completo e fazer a solução mais ineficiente, horrivelmente-overmetaprogrammed podemos chegar a ... no interesse da sintática
purezahamfistery. Com os objetos ES6 Proxy! ... Vamos também definir algumas propriedades que (imho são boas e maravilhosas, mas) podem quebrar bibliotecas gravadas incorretamente. Talvez você deva ter cuidado ao usar isso se se preocupa com desempenho, sanidade (sua ou de outras pessoas), seu trabalho, etc.Demo:
Resultado:
idéia ineficiente: você pode modificar o item acima para enviar com base no argumento de entrada; use o
.match(/[^\]\[.]+/g)
método para dar suporteobj['keys'].like[3]['this']
ouinstanceof Array
, se aceitar, apenas uma matriz como entradakeys = ['a','b','c']; obj.H[keys]
.Por sugestão de que talvez você queira manipular índices indefinidos de uma maneira 'suave' no estilo NaN (por exemplo,
index({a:{b:{c:...}}}, 'a.x.c')
retornar indefinido em vez de TypeError não detectado) ...:1) Isso faz sentido da perspectiva de "devemos retornar indefinidos em vez de gerar um erro" na situação do índice unidimensional ({}) ['eg'] == indefinido, portanto "devemos retornar indefinidos em vez de lançar um erro "na situação N-dimensional.
2) Isso não faz sentido da perspectiva que estamos fazendo
x['a']['x']['c']
, o que falharia com um TypeError no exemplo acima.Dito isso, você faria esse trabalho substituindo sua função de redução por:
(o,i)=>o===undefined?undefined:o[i]
, Ou(o,i)=>(o||{})[i]
.(Você pode tornar isso mais eficiente usando um loop for e interrompendo / retornando sempre que o sub-resultado no qual você indexar o próximo for indefinido ou usando um try-catch se você espera que essas falhas sejam suficientemente raras.)
fonte
reduce
não é suportado em todos os navegadores usados atualmente.Array.reduce
faz parte do padrão ECMA-262. Se você realmente deseja oferecer suporte a navegadores desatualizados, pode definirArray.prototype.reduce
a implementação de amostra fornecida em algum lugar (por exemplo, developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… ).var setget = function( obj, path ){ function index( robj,i ) {return robj[i]}; return path.split('.').reduce( index, obj ); }
Se você pode usar o lodash , existe uma função que faz exatamente isso:
_.get (objeto, caminho, [defaultValue])
fonte
_.get(object, path)
não quebra se um caminho não foi encontrado.'a.b.etc'.split('.').reduce((o,i)=>o[i], obj)
faz. Para o meu caso específico - não para todos os casos - exatamente o que eu precisava. Obrigado!defaultValue
. O_.get()
método retorna o valor padrão se for_.get()
resolvidoundefined
, então defina-o como desejar e observe o valor definido._.set(object, path, value)
.Um exemplo um pouco mais envolvido com recursão.
fonte
você também pode usar o lodash.get
Você acabou de instalar este pacote (npm i --save lodash.get) e depois o usa assim:
fonte
Se você espera desreferenciar o mesmo caminho muitas vezes, a criação de uma função para cada caminho de notação de ponto realmente tem o melhor desempenho (de longe, expandindo os testes de desempenho aos quais James Wilkins vinculou nos comentários acima).
O uso do construtor Function apresenta as mesmas desvantagens que eval () em termos de segurança e pior desempenho, mas o IMO é uma ferramenta pouco utilizada para casos em que você precisa de uma combinação de extremo dinamismo e alto desempenho. Eu uso essa metodologia para criar funções de filtro de matriz e chamá-las dentro de um loop de resumo do AngularJS. Meus perfis mostram consistentemente a etapa array.filter () que leva menos de 1ms para desreferenciar e filtrar cerca de 2000 objetos complexos, usando caminhos definidos dinamicamente com níveis de profundidade de 3 a 4.
Uma metodologia semelhante poderia ser usada para criar funções de setter, é claro:
fonte
Muitos anos desde o post original. Agora existe uma ótima biblioteca chamada 'caminho do objeto'. https://github.com/mariocasciaro/object-path
Disponível no NPM e no BOWER https://www.npmjs.com/package/object-path
É tão fácil quanto:
E funciona para propriedades e matrizes profundamente aninhadas.
fonte
Sugiro dividir o caminho, iterá-lo e reduzir o objeto que você possui. Esta proposta trabalha com um valor padrão para propriedades ausentes.
fonte
Observe que se você já estiver usando o Lodash, poderá usar as funções
property
ouget
:Sublinhado também tem uma
property
função, mas não suporta notação de ponto.fonte
Outras propostas são um pouco enigmáticas, então pensei em contribuir:
fonte
Você pode usar a biblioteca disponível no npm, o que simplifica esse processo. https://www.npmjs.com/package/dot-object
fonte
Isso é muito código quando comparado à
eval
maneira mais simples de fazer isso, mas como Simon Willison diz, você nunca deve usar eval .Além disso, o JSFiddle .
fonte
Estendi a resposta elegante por ninjagecko para que a função lide com referências pontilhadas e / ou de estilo de matriz e para que uma string vazia faça com que o objeto pai seja retornado.
Aqui está:
Veja meu exemplo de trabalho do jsFiddle aqui: http://jsfiddle.net/sc0ttyd/q7zyd/
fonte
[]
notação é sempre para matrizes. não pode haver chaves de objeto representado dessa forma também, por exemploobj['some-problem/name'].list[1]
Para corrigir isso eu tinha de atualizaçãoarr_deref
função como estajavascript function arr_deref(o, ref, i) { return !ref ? o : (o[(ref.slice(0, i ? -1 : ref.length)).replace(/^['"]|['"]$/g, '')]); }
fonte
Você pode obter o valor de um membro do objeto por notação de ponto com uma única linha de código:
No seu caso:
Para simplificar, você pode escrever uma função como esta:
Explicação:
O construtor Function cria um novo objeto Function. Em JavaScript, toda função é realmente um objeto Function. A sintaxe para criar uma função explicitamente com o construtor Function é:
onde
arguments(arg1 to argN)
deve haver uma sequência que corresponda a um identificador javaScript válido efunctionBody
é uma sequência que contém as instruções javaScript que compreendem a definição da função.No nosso caso, aproveitamos a vantagem do corpo da função string para recuperar o membro do objeto com notação de ponto.
Espero que ajude.
fonte
Resposta GET / SET que também funciona em reagir nativo (você não pode atribuir a
Object.prototype
atualmente):Uso:
fonte
Copiei o seguinte da resposta de Ricardo Tomasi e modifiquei para também criar subobjetos que ainda não existem conforme necessário. É um pouco menos eficiente (mais
if
cria objetos vazios), mas deve ser muito bom.Além disso, nos permitirá fazer
Object.prop(obj, 'a.b', false)
onde não podíamos antes. Infelizmente, ele ainda não nos permite atribuirundefined
... Ainda não sabemos como proceder.fonte
Se você deseja converter qualquer objeto que contenha chaves de notação de ponto em uma versão em matriz dessas chaves, você pode usá-lo.
Isso converterá algo como
para
fonte
Aqui está o meu código sem usar
eval
. É fácil de entender também.fonte
Sim, foi perguntado há 4 anos e sim, estender protótipos de base geralmente não é uma boa ideia, mas, se você mantiver todas as extensões em um só lugar, elas poderão ser úteis.
Então, aqui está a minha maneira de fazer isso.
Agora você poderá obter propriedades aninhadas em qualquer lugar sem importar o módulo com a função ou a função copiar / colar.
UPD.Exemplo:
UPD 2. A próxima extensão interrompe o mangusto no meu projeto. Também li que pode ter quebrado o jquery. Portanto, nunca faça da próxima maneira
fonte
Aqui está a minha implementação
Implementação 1
Implementação 2 (usando matriz reduzida em vez de fatia)
Exemplos:
ele também pode manipular objetos dentro de matrizes como para
fonte
Esta é a minha solução estendida proposta por: ninjagecko
Para mim, a simples notação de seqüência de caracteres não foi suficiente; portanto, a versão abaixo suporta coisas como:
índice (obj, 'data.accounts [0] .address [0] .postcode');
fonte
Correndo o risco de bater em um cavalo morto ... acho isso mais útil ao atravessar objetos aninhados para fazer referência a onde você está com relação ao objeto base ou a um objeto semelhante com a mesma estrutura. Para esse fim, isso é útil com uma função de travessia de objeto aninhado. Observe que eu usei uma matriz para manter o caminho. Seria trivial modificar isso para usar um caminho de string ou uma matriz. Observe também que você pode atribuir "indefinido" ao valor, diferentemente de outras implementações.
fonte
Eu usei esse código no meu projeto
Uso:
fonte
Poucos anos depois, descobri isso que lida com escopo e matriz. por exemplo
a['b']["c"].d.etc
fonte
Não está claro qual é a sua pergunta. Dado o seu objeto,
obj.a.b
daria a você "2" exatamente como é. Se você quiser manipular a string para usar colchetes, faça o seguinte:fonte
a.b.c
e realmente não realiza o que eles querem. Eles querem o valor, não umeval
caminho.a.b.c
, mas você está certo, aparentemente ele queria obter / definir o valor da propriedade emobj.a.b
. A questão foi confuso para mim, já que ele disse que queria "converter a string" ....aqui estão os meus 10 centavos de caso, a função abaixo será configurada com base no caminho fornecido, .. com certeza você pode melhorá-lo, remova || e substitua-o por
Object.hasOwnProperty
se você se importa com valores falsos por engano,Eu testei com
a.b.c
e ab2.c{a:{b:[0,1,{c:7}]}}
e seus trabalhos para definir e obter :).cheerz
fonte