[Javascript] funzione mappa per oggetti (anziché array)


Answers

Che ne dici di un solo rivestimento con assegnazione diretta delle variabili in JS normale ( ES6 / ES2015 )?

Facendo uso dell'operatore di spread e della sintassi del nome della chiave calcolata :

let newObj = Object.assign({}, ...Object.keys(obj).map(k => ({[k]: obj[k] * obj[k]})));

jsbin

Un'altra versione che utilizza riduce:

let newObj = Object.keys(obj).reduce((p, c) => ({...p, [c]: obj[c] * obj[c]}), {});

jsbin

Primo esempio come funzione:

const oMap = (o, f) => Object.assign({}, ...Object.keys(o).map(k => ({ [k]: f(o[k]) })));

// To square each value you can call it like this:
let mappedObj = oMap(myObj, (x) => x * x);

jsbin

Se si desidera mappare ricorsivamente un oggetto nidificato in uno stile funzionale , può essere fatto in questo modo:

const sqrObjRecursive = (obj) => 
  Object.keys(obj).reduce((newObj, key) => 
    (obj[key] && typeof obj[key] === 'object') ?
      {...newObj, [key]: sqrObjRecursive(obj[key])} :  // recurse.
      {...newObj, [key]: obj[key] * obj[key]}          // square val.
    ,{})       

jsbin

O più imperativamente, come questo:

const sqrObjRecursive = (obj) => {
  Object.keys(obj).forEach(key => {
    if (typeof obj[key] === 'object') obj[key] = sqrObjRecursive(obj[key]);
    else obj[key] = obj[key] * obj[key]
  });
  return obj;
};

jsbin

Poiché ES7 / ES2016 è possibile utilizzare Object.entries anziché Object.keys ad esempio in questo modo:

let newObj = Object.assign(...Object.entries(obj).map(([k, v]) => ({[k]: v * v})));


Proprietà ereditate e catena del prototipo:

In alcune rare situazioni potrebbe essere necessario mappare un oggetto simile alla classe che contiene le proprietà di un oggetto ereditato sulla sua prototype-chain . In questi casi, Object.keys() non funzionerà, poiché Object.keys() non enumera le proprietà ereditate . Se hai bisogno di mappare le proprietà ereditate , dovresti usare for (key in myObj) {...} .

Ecco un esempio di un oggetto che eredita le proprietà di un altro oggetto e di come Object.keys() non funziona in tale scenario.

const obj1 = { 'a': 1, 'b': 2, 'c': 3}
const obj2 = Object.create(obj1);  // One of multiple ways to inherit an object in JS.

// Here you see how the properties of obj1 sit on the 'prototype' of obj2
console.log(obj2)  // Prints: obj2.__proto__ = { 'a': 1, 'b': 2, 'c': 3}

console.log(Object.keys(obj2));  // Prints: an empty Array.

for (key in obj2) {
  console.log(key);              // Prints: 'a', 'b', 'c'
}

jsbin

Tuttavia, per favore fammi un favore ed evita l' eredità . :-)

Question

Ho un oggetto:

myObject = { 'a': 1, 'b': 2, 'c': 3 }

Sto cercando un metodo nativo, simile a Array.prototype.map che sarebbe usato come segue:

newObject = myObject.map(function (value, label) {
    return value * value;
});

// newObject is now { 'a': 1, 'b': 4, 'c': 9 }

JavaScript ha una funzione map per oggetti? (Lo voglio per Node.JS, quindi non mi preoccupo dei problemi di cross-browser.)




const orig = { 'a': 1, 'b': 2, 'c': 3 }

const result = _.transform(orig, (r, v, k) => r[k.trim()] = v * 2);

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

Usa il nuovo _.transform () per trasformare l'oggetto .




Sono venuto qui cercando di trovare e rispondere per mappare un oggetto su un array e ho ottenuto questa pagina come risultato. Nel caso venissi qui cercando la stessa risposta che ero, ecco come puoi mappare e obiettare a un array.

Puoi usare map per restituire un nuovo array dall'oggetto in questo modo:

var newObject = Object.keys(myObject).map(function(key) {
   return myObject[key];
});



È abbastanza facile scriverne uno:

Object.map = function(o, f, ctx) {
    ctx = ctx || this;
    var result = {};
    Object.keys(o).forEach(function(k) {
        result[k] = f.call(ctx, o[k], k, o); 
    });
    return result;
}

con il codice di esempio:

> o = { a: 1, b: 2, c: 3 };
> r = Object.map(o, function(v, k, o) {
     return v * v;
  });
> r
{ a : 1, b: 4, c: 9 }

NB: questa versione consente anche di (facoltativamente) impostare il contesto per la richiamata, proprio come il metodo Array .

MODIFICA - modificato per rimuovere l'uso di Object.prototype , per garantire che non si scontri con alcuna proprietà esistente denominata map sull'oggetto.




La map function non esiste su Object.prototype tuttavia è possibile emularlo in questo modo

var myMap = function ( obj, callback ) {

    var result = {};

    for ( var key in obj ) {
        if ( Object.prototype.hasOwnProperty.call( obj, key ) ) {
            if ( typeof callback === 'function' ) {
                result[ key ] = callback.call( obj, obj[ key ], key, obj );
            }
        }
    }

    return result;

};

var myObject = { 'a': 1, 'b': 2, 'c': 3 };

var newObject = myMap( myObject, function ( value, key ) {
    return value * value;
});



Se ti interessa il ping della map non solo i valori ma anche le chiavi, ho scritto Object.map(valueMapper, keyMapper) , che si comporta in questo modo:

var source = { a: 1, b: 2 };
function sum(x) { return x + x }

source.map(sum);            // returns { a: 2, b: 4 }
source.map(undefined, sum); // returns { aa: 1, bb: 2 }
source.map(sum, sum);       // returns { aa: 2, bb: 4 }



È possibile utilizzare un semplice ciclo for-in tramite coppie ordinate. Ho usato hasOwnProperty() perché hai creato tre proprietà (con valori) per il tuo oggetto. Il primo metodo non crea una mappa. Invece, applica semplicemente la funzione a un singolo elemento, che può velocizzare notevolmente l'esecuzione in molte situazioni.

Il secondo metodo crea una mappa in modo simile al primo, ma probabilmente è più lenta di altre risposte qui.

var myObject = { 'a': 1, 'b': 2, 'c': 3 }

//Doesn't create a map, just applies the function to a specific element
function getValue(key) {
  for (var k in myObject) {
    if (myObject.hasOwnProperty(key)) {
      var value = myObject[key]
      return value * value; //stops iteration
    }
  }
}

//creates a map (answers question, but above function is better in some situations)
var newObject = {};
makeMap();

function makeMap() {
    for (var k in myObject) {
        var value = myObject[k];
        newObject[k] = value * value;
    }
}

console.log(newObject); //mapped array
Input: <input id="input" value="" placeholder="a, b or c"><br>
Output:<input id="output"><br>
<button onclick="output.value=getValue(input.value)" >Get value</button>




Versione minima (es6):

Object.entries(obj).reduce((a, [k, v]) => (a[k] = v * v, a), {})



var myObject = { 'a': 1, 'b': 2, 'c': 3 };


Object.prototype.map = function(fn){
    var oReturn = {};
    for (sCurObjectPropertyName in this) {
        oReturn[sCurObjectPropertyName] = fn(this[sCurObjectPropertyName], sCurObjectPropertyName);
    }
    return oReturn;
}
Object.defineProperty(Object.prototype,'map',{enumerable:false});





newObject = myObject.map(function (value, label) {
    return value * value;
});


// newObject is now { 'a': 1, 'b': 4, 'c': 9 }



La risposta accettata ha due inconvenienti:

  • Array.prototype.reduce , perché ridurre significa cambiare la struttura di un tipo composito, cosa che non accade in questo caso.
  • Non è particolarmente riutilizzabile

Un approccio funzionale ES6 / ES2015

Si noti che tutte le funzioni sono definite in forma al curry.

// small, reusable auxiliary functions

const keys = o => Object.keys(o);

const assign = (...o) => Object.assign({}, ...o);

const map = f => xs => xs.map(x => f(x));

const mul = y => x => x * y;

const sqr = x => mul(x) (x);


// the actual map function

const omap = f => o => {
  o = assign(o); // A
  map(x => o[x] = f(o[x])) (keys(o)); // B
  return o;
};


// mock data

const o = {"a":1, "b":2, "c":3};


// and run

console.log(omap(sqr) (o));
console.log(omap(mul(10)) (o));

  • Nella riga A o viene riassegnato. Poiché Javascript passa i valori di riferimento condividendo , viene generata una copia superficiale di o . Ora siamo in grado di mutare o all'interno di omap senza mutare o nell'ambito genitore.
  • Nel valore di ritorno della map B della linea viene ignorato, perché la map esegue una mutazione di o . Poiché questo effetto collaterale rimane all'interno di omap e non è visibile nell'ambito genitore, è totalmente accettabile.

Questa non è la soluzione più veloce, ma è dichiarativa e riutilizzabile. Ecco la stessa implementazione di una riga, succinta ma meno leggibile:

const omap = f => o => (o = assign(o), map(x => o[x] = f(o[x])) (keys(o)), o);

Addendum: perché gli oggetti non sono iterabili per impostazione predefinita?

ES2015 ha specificato l'iteratore e i protocolli iterabili. Ma gli oggetti non sono ancora iterabili e quindi non mappabili. Il motivo è il mix di dati e livello di programma .




Avevo bisogno di una versione che permettesse di modificare anche le chiavi (basata sulle risposte di @Amberlamps e @yonatanmn);

var facts = [ // can be an object or array - see jsfiddle below
    {uuid:"asdfasdf",color:"red"},
    {uuid:"sdfgsdfg",color:"green"},
    {uuid:"dfghdfgh",color:"blue"}
];

var factObject = mapObject({}, facts, function(key, item) {
    return [item.uuid, {test:item.color, oldKey:key}];
});

function mapObject(empty, obj, mapFunc){
    return Object.keys(obj).reduce(function(newObj, key) {
        var kvPair = mapFunc(key, obj[key]);
        newObj[kvPair[0]] = kvPair[1];
        return newObj;
    }, empty);
}

factObject =

{
"asdfasdf": {"color":"red","oldKey":"0"},
"sdfgsdfg": {"color":"green","oldKey":"1"},
"dfghdfgh": {"color":"blue","oldKey":"2"}
}

Modifica: leggera modifica per passare nell'oggetto iniziale {}. Consente di essere [] (se le chiavi sono numeri interi)