Matrices asociativas multidimensionales en JavaScript

84

Existen los siguientes resultados de la consulta: (key1 y key2 podrían ser cualquier texto)

id   key1     key2     value

1    fred     apple    2
2    mary     orange   10
3    fred     banana   7
4    fred     orange   4
5    sarah    melon    5
...

y deseo almacenar los datos en una cuadrícula (tal vez como una matriz) repitiendo todos los registros como este:

         apple    orange   banana  melon
fred        2        4         7     -
mary        -        10        -     -
sarah       -        -         -     5

En PHP esto sería realmente fácil, usando matrices asociativas:

$result['fred']['apple'] = 2;

Pero en matrices asociativas de JavaScript como esta no funciona. Después de leer toneladas de tutoriales, todo lo que pude obtener fue esto:

arr=[];
arr[1]['apple'] = 2;

pero arr['fred']['apple'] = 2;no funciona. Probé matrices de objetos, pero las propiedades de los objetos no pueden ser texto libre. Cuanto más leía los tutoriales, más me confundía ...

Cualquier idea es bienvenida :)

Omiod
fuente
Gracias por las respuestas, pero estoy revisando los resultados de la consulta y deseo establecer los valores uno a la vez. Las líneas de ejemplo (tomadas como ejemplo de Matt) var grid = {};grid['aa']['bb'] = 1;devuelven "Uncaught TypeError: No se puede establecer la propiedad 'bb' de indefinido". Podría estar equivocado, pero con la mayoría de sus ejemplos tengo que conocer los datos en el momento de la inicialización.
Omiod
Acabo de encontrar que var grid = {}; grid['aa'] = {}; grid['aa']['bb'] = 1;funciona. Una prueba más compleja falla, pero parece que estoy en el camino correcto
Omiod
2
primero debe inicializar el subobjeto, como mencioné en mi respuesta. var grid = {}; grd ['aa'] = {}; luego puede hacer grid ['aa'] ['bb'] = 1. Hay muchas formas de verificar si el subobjeto ya está inicializado (como se menciona en mi respuesta), por lo que no sobrescribe un objeto.
Matt
actualicé mi respuesta con un código adicional. no estoy seguro de qué tan profundos son sus objetos, o cómo está obteniendo sus datos, pero con suerte lo señalará en la dirección correcta
Matt

Respuestas:

152

Simplemente use un objeto JavaScript normal, que se 'leería' de la misma manera que sus matrices asociativas. También debe recordar inicializarlos primero.

var obj = {};

obj['fred'] = {};
if('fred' in obj ){ } // can check for the presence of 'fred'
if(obj.fred) { } // also checks for presence of 'fred'
if(obj['fred']) { } // also checks for presence of 'fred'

// The following statements would all work
obj['fred']['apples'] = 1;
obj.fred.apples = 1;
obj['fred'].apples = 1;

// or build or initialize the structure outright
var obj = { fred: { apples: 1, oranges: 2 }, alice: { lemons: 1 } };

Si está examinando valores, es posible que tenga algo parecido a esto:

var people = ['fred', 'alice'];
var fruit = ['apples', 'lemons'];

var grid = {};
for(var i = 0; i < people.length; i++){
    var name = people[i];
    if(name in grid == false){
        grid[name] = {}; // must initialize the sub-object, otherwise will get 'undefined' errors
    }

    for(var j = 0; j < fruit.length; j++){
        var fruitName = fruit[j];
        grid[name][fruitName] = 0;
    }
}
Mate
fuente
Se las arregló para usar el ejemplo en el código de producción (una extensión de Chrome) y funciona bien. Gracias. ¡Finalmente estoy aprendiendo cómo manejar objetos en JS!
Omiod
3
¡No puedo votar esta respuesta lo suficiente! La parte que estaba causando que mi objeto regresara continuamente indefinido era que NO estaba inicializando el subobjeto. ¡Así que asegúrate de hacer esto! ejemplo: grid[name] = {};
Jason Pudzianowski
1
@Matt ¿Hay alguna forma de obtener obj.fred.apples usando solo un conjunto de []? Asumo que no. obj["fred.apples"]por ejemplo
theB3RV
¿Qué pasa si no sabemos el número de la matriz, por ejemplo su código [javasript] var people = ['fred', 'alice']; var fruit = ['manzanas', 'limones']; var color = ['red', 'blue'] var ..... -> el número de matriz es desconocido [/ javascript]
YukiSakura
¿Puedo acceder al primer elemento en obj (fred) por índice numérico? Como obj [0] que da como resultado manzanas: 1, naranjas: 2. ¿Puedo acceder al último elemento (que es Alice y da como resultado limones: 1)? ''
Timo
26

Si no tiene que ser una matriz, puede crear un objeto JS "multidimensional" ...

<script type="text/javascript">
var myObj = { 
    fred: { apples: 2, oranges: 4, bananas: 7, melons: 0 }, 
    mary: { apples: 0, oranges: 10, bananas: 0, melons: 0 }, 
    sarah: { apples: 0, oranges: 0, bananas: 0, melons: 5 } 
}

document.write( myObject[ 'fred' ][ 'apples' ] );
</script>
charliegriefer
fuente
JSON es en realidad el formato 'en cadena' del objeto JavaScript. Lo que tienes allí no es JSON, solo un objeto JavaScript.
Matt
Gracias, Matt. Actualizó la publicación.
charliegriefer
1
@charliegriefer, ¿no debería ser la línea document.write: "document.write (myObj ......"
juan
13

Javascript es flexible:

var arr = {
  "fred": {"apple": 2, "orange": 4},
  "mary": {}
  //etc, etc
};

alert(arr.fred.orange);
alert(arr["fred"]["orange"]);
for (key in arr.fred)
    alert(key + ": " + arr.fred[key]);
cambraca
fuente
8
Yo diría que el nombre de la variable 'arr' es un nombre inapropiado aquí, porque en realidad no es una matriz, sino un objeto.
Matt
Soy un perro vago y por lo tanto no puedo usar tu solución. Si hubiera estado ansioso, encontraría mi camino con tu ejemplo de submatriz;) De todos modos, gracias por tu esfuerzo.
Timo
9

Como necesitaba obtener todos los elementos de una manera agradable, encontré este tema SO "Atravesando matriz / objeto asociativo bidimensional", sin importar el nombre para mí, porque la funcionalidad cuenta.

var imgs_pl = { 
    'offer':        { 'img': 'wer-handwritter_03.png', 'left': 1, 'top': 2 },
    'portfolio':    { 'img': 'wer-handwritter_10.png', 'left': 1, 'top': 2 },
    'special':      { 'img': 'wer-handwritter_15.png', 'left': 1, 'top': 2 }
};
for (key in imgs_pl) { 
    console.log(key);
    for (subkey in imgs_pl[key]) { 
        console.log(imgs_pl[key][subkey]);
    }
}
Michal - wereda-net
fuente
Debería aceptarse la respuesta porque un bucle suele ser el camino a seguir.
Timo
5

Parece que para algunas aplicaciones, existe un enfoque mucho más simple para las matrices asociativas multidimensionales en javascript.

  1. Dado que la representación interna de todas las matrices son en realidad objetos de objetos, se ha demostrado que el tiempo de acceso para elementos indexados numéricamente es en realidad el mismo que para los elementos indexados asociativos (texto).

  2. el tiempo de acceso para elementos indexados asociativos de primer nivel no aumenta a medida que aumenta el número de elementos reales.

Dado esto, puede haber muchos casos en los que en realidad sea mejor utilizar un enfoque de cadena concatenada para crear la equivalencia de elementos multidimensionales. Por ejemplo:

store['fruit']['apples']['granny']['price] = 10
store['cereal']['natural']['oats']['quack'] = 20

va a:

store['fruit.apples.granny.price'] = 10
store['cereal.natural.oats.quack'] = 20

Las ventajas incluyen:

  • no es necesario inicializar subobjetos o averiguar cómo combinar mejor los objetos
  • tiempo de acceso de un solo nivel. los objetos dentro de los objetos necesitan N veces el tiempo de acceso
  • puede usar Object.keys () para extraer toda la información de dimensión y ..
  • puede usar la función regex.test (cadena) y la función array.map en las teclas para extraer exactamente lo que desea.
  • sin jerarquía en las dimensiones.
  • el "punto" es arbitrario: el uso de subrayado en realidad hace que las expresiones regulares sean más fáciles
  • También hay muchos scripts para "acoplar" JSON dentro y fuera de este formato.
  • puede usar todas las otras funciones agradables de procesamiento de matrices en la lista de teclas
sdw
fuente
3

Obtenga el valor de la propiedad de una matriz de matrices asociativas cuando el nombre de la propiedad es un número entero:

Comenzando con una matriz asociativa donde los nombres de las propiedades son números enteros:

var categories = [
    {"1":"Category 1"},
    {"2":"Category 2"},
    {"3":"Category 3"},
    {"4":"Category 4"}
];

Empuje elementos a la matriz:

categories.push({"2300": "Category 2300"});
categories.push({"2301": "Category 2301"});

Recorra la matriz y haga algo con el valor de la propiedad.

for (var i = 0; i < categories.length; i++) {
    for (var categoryid in categories[i]) {
        var category = categories[i][categoryid];
        // log progress to the console
        console.log(categoryid + " : " + category);
        //  ... do something
    }
}

La salida de la consola debería verse así:

1 : Category 1
2 : Category 2
3 : Category 3
4 : Category 4
2300 : Category 2300
2301 : Category 2301

Como puede ver, puede evitar la limitación de la matriz asociativa y hacer que el nombre de una propiedad sea un número entero.

NOTA: La matriz asociativa en mi ejemplo es el json que tendría si serializara un objeto Dictionary [].

Jason Williams
fuente
3

No use una matriz, use un objeto.

var foo = new Object();
Tim
fuente
2
no use un new Object(), ya que el Object.prototype podría tener una rareza adjunta; use el objeto literal:var foo = {};
Michael Paulukonis
1
@Michael Pensé que {} era solo azúcar sintáctico para new Object (); Al igual que [] es la abreviatura de new Array ()
Matt
1
desde mi consola Chrome JS, ambas construcciones parecen idénticas, incluidos sus prototipos. Incluso la adición de Object.prototype.foo = function () {} aparece en ambos.
Matt
1
@ Matt Creo que estoy equivocado, estaba mezclando eso con lo new Array()que debería evitarse. ugh.
Michael Paulukonis
1
@Michael ¿por qué debería evitarse el nuevo Array ()?
Matt
2

No es necesario utilizar objetos necesariamente, puede hacerlo con matrices multidimensionales normales.

Esta es mi solución sin objetos :

// Javascript
const matrix = [];

matrix.key1 = [
  'value1',
  'value2',
];

matrix.key2 = [
  'value3',
];

que en PHP es equivalente a:

// PHP
$matrix = [
    "key1" => [
        'value1',
        'value2',
    ],
    "key2" => [
        'value3',
    ]
];
Francesco Borzi
fuente
1
¿Cómo haces un bucle (usa cada uno) esto más adelante?
Sagive SEO
0

<script language="javascript">

// Set values to variable
var sectionName = "TestSection";
var fileMap = "fileMapData";
var fileId = "foobar";
var fileValue= "foobar.png";
var fileId2 = "barfoo";
var fileValue2= "barfoo.jpg";

// Create top-level image object
var images = {};

// Create second-level object in images object with
// the name of sectionName value
images[sectionName] = {};

// Create a third level object
var fileMapObj = {};

// Add the third level object to the second level object
images[sectionName][fileMap] = fileMapObj;

// Add forth level associate array key and value data
images[sectionName][fileMap][fileId] = fileValue;
images[sectionName][fileMap][fileId2] = fileValue2;


// All variables
alert ("Example 1 Value: " + images[sectionName][fileMap][fileId]);

// All keys with dots
alert ("Example 2 Value: " + images.TestSection.fileMapData.foobar);

// Mixed with a different final key
alert ("Example 3 Value: " + images[sectionName]['fileMapData'][fileId2]);

// Mixed brackets and dots...
alert ("Example 4 Value: " + images[sectionName]['fileMapData'].barfoo);

// This will FAIL! variable names must be in brackets!
alert ("Example 5 Value: " + images[sectionName]['fileMapData'].fileId2);
// Produces: "Example 5 Value: undefined".

// This will NOT work either. Values must be quoted in brackets.
alert ("Example 6 Value: " + images[sectionName][fileMapData].barfoo);
// Throws and exception and stops execution with error: fileMapData is not defined

// We never get here because of the uncaught exception above...
alert ("The End!");
</script>

Rich Schramm
fuente
0
    var myObj = [];
    myObj['Base'] = [];
    myObj['Base']['Base.panel.panel_base'] = {ContextParent:'',ClassParent:'',NameParent:'',Context:'Base',Class:'panel',Name:'panel_base',Visible:'',ValueIst:'',ValueSoll:'',
                                              Align:'',  AlignFrom:'',AlignTo:'',Content:'',onClick:'',Style:'',content_ger_sie:'',content_ger_du:'',content_eng:'' };
    myObj['Base']['Base.panel.panel_top']  = {ContextParent:'',ClassParent:'',NameParent:'',Context:'Base',Class:'panel',Name:'panel_base',Visible:'',ValueIst:'',ValueSoll:'',
                                              Align:'',AlignFrom:'',AlignTo:'',Content:'',onClick:'',Style:'',content_ger_sie:'',content_ger_du:'',content_eng:'' };

    myObj['SC1'] = [];
    myObj['SC1']['Base.panel.panel_base'] = {ContextParent:'',ClassParent:'',NameParent:'',Context:'Base',Class:'panel',Name:'panel_base',Visible:'',ValueIst:'',ValueSoll:'',
                                              Align:'',  AlignFrom:'',AlignTo:'',Content:'',onClick:'',Style:'',content_ger_sie:'',content_ger_du:'',content_eng:'' };
    myObj['SC1']['Base.panel.panel_top']  = {ContextParent:'',ClassParent:'',NameParent:'',Context:'Base',Class:'panel',Name:'panel_base',Visible:'',ValueIst:'',ValueSoll:'',
                                              Align:'',AlignFrom:'',AlignTo:'',Content:'',onClick:'',Style:'',content_ger_sie:'',content_ger_du:'',content_eng:'' };


    console.log(myObj);

    if ('Base' in myObj) {
      console.log('Base found');

      if ('Base.panel.panel_base' in myObj['Base'])  {
        console.log('Base.panel.panel_base found'); 


      console.log('old value: ' + myObj['Base']['Base.panel.panel_base'].Context);  
      myObj['Base']['Base.panel.panel_base'] = 'new Value';
      console.log('new value: ' + myObj['Base']['Base.panel.panel_base']);
      }
    }

Salida:

  • Base encontrada
  • Base.panel.panel_base encontrado
  • valor anterior: Base
  • nuevo valor: nuevo valor

La operación de matriz funciona. No hay ningún problema.

Iteración:

     Object.keys(myObj['Base']).forEach(function(key, index) {            
        var value = objcons['Base'][key];                   
      }, myObj);
Necips
fuente
2
Bienvenido a stackoverflow. Edite su respuesta y explique por qué sus fragmentos de código resuelven el problema. Más información aquí: Cómo responder
Djensen