Mapear sobre chaves de preservação de objeto

129

A mapfunção em underscore.js, se chamada com um objeto javascript, retorna uma matriz de valores mapeados a partir dos valores do objeto.

_.map({one: 1, two: 2, three: 3}, function(num, key){ return num * 3; });
=> [3, 6, 9]

existe uma maneira de fazê-lo preservar as chaves? ou seja, eu quero uma função que retorne

{one: 3, two: 6, three: 9}
xuanji
fonte
Se você, como eu, veio aqui procurando por uma função semelhante a 'mapValues' que altera o objeto real em vez de retornar um novo, verifique esta solução simples: stackoverflow.com/questions/30894044/…
Michael Trouw

Respostas:

228

Com sublinhado

O sublinhado fornece uma função _.mapObjectpara mapear os valores e preservar as chaves.

_.mapObject({ one: 1, two: 2, three: 3 }, function (v) { return v * 3; });

// => { one: 3, two: 6, three: 9 }

DEMO


Com Lodash

O Lodash fornece uma função _.mapValuespara mapear os valores e preservar as chaves.

_.mapValues({ one: 1, two: 2, three: 3 }, function (v) { return v * 3; });

// => { one: 3, two: 6, three: 9 }

DEMO

GG.
fonte
Houve um esforço para colocar uma função _.mapValues ​​em sublinhado: Problema Relevante , Solicitação Pull para _.mapValues . Espero que isso passe :)
raffomania
parece-me que você está transformando um objeto em um objeto, ou estou louco?
JSH
@jsh Nesse caso, _.map()está retornando [['one', 3], ['two', 6], ['three', 9]], que é uma matriz de matrizes, e a _.object()transforma em um objeto.
precisa
56

Consegui encontrar a função necessária no lodash, uma biblioteca de utilidades semelhante ao sublinhado.

http://lodash.com/docs#mapValues

_.mapValues(object, [callback=identity], [thisArg])

Cria um objeto com as mesmas chaves que o objeto e os valores gerados executando cada propriedade enumerável do objeto por meio do retorno de chamada. O retorno de chamada é vinculado a thisArg e invocado com três argumentos; (valor, chave, objeto).

xuanji
fonte
19

var mapped = _.reduce({ one: 1, two: 2, three: 3 }, function(obj, val, key) {
    obj[key] = val*3;
    return obj;
}, {});

console.log(mapped);
<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>

kunalgolani
fonte
13

Eu sei que isso é antigo, mas agora o Underscore tem um novo mapa para objetos:

_.mapObject(object, iteratee, [context]) 

Obviamente, você pode criar um mapa flexível para matrizes e objetos

_.fmap = function(arrayOrObject, fn, context){
    if(this.isArray(arrayOrObject))
      return _.map(arrayOrObject, fn, context);
    else
      return _.mapObject(arrayOrObject, fn, context);
}
Rayjax
fonte
5
Nota aos usuários do lodash: o jdalton decidiu quebrar a compatibilidade com o underscore.js nessa alteração. lodash não suporta mapObject; observe o mapValuesmétodo de lodash .
Mark Amery
13

Que tal esta versão em JS simples ( ES6 / ES2015 )?

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

jsbin

Se você deseja mapear sobre um objeto recursivamente (mapear objeto aninhado), isso pode ser feito assim:

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

jsbin

Desde o ES7 / ES2016, você pode usar em Object.entriesvez de Object.keys:

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

Eu sei que já faz muito tempo, mas ainda está faltando a solução mais óbvia via fold (também conhecida como redução em js), por uma questão de integridade, deixarei aqui:

function mapO(f, o) {
  return Object.keys(o).reduce((acc, key) => {
    acc[key] = f(o[key])
    return acc
  }, {})
}
Darwin
fonte
Eu estou bem com o uso da biblioteca Lodash, mas os programadores estão usando esse tipo de biblioteca e sem saber como isso pode ser alcançado no vanilla JS. Então, agradeço sua resposta!
cyonder
3

_.map retorna uma matriz, não um objeto.

Se você quer um objeto, é melhor usar uma função diferente, como each; se você realmente quiser usar o mapa, poderá fazer algo assim:

Object.keys(object).map(function(value, index) {
   object[value] *= 3;
})

mas isso é confuso, ao ver mapum seria esperado ter uma matriz como resultado e, em seguida, fazer algo com ela.

Alberto Zaccagni
fonte
Ao ler os documentos, tive a sensação de que não seria natural tentar isso em underscore.js. Eu acho que meu caso de uso é bastante natural, por que eles não o suportam?
Xuanji # 26/13
Provavelmente, um motivo pode ser mapusado para alterar uma entrada que produz uma matriz como saída, você pode compor _.objecte _.mapcomo @GG. escreveu, mas isso é uma questão de gosto neste momento.
Alberto Zaccagni 26/09
3

Eu acho que você deseja uma função mapValues (para mapear uma função sobre os valores de um objeto), que é fácil o suficiente para se implementar:

mapValues = function(obj, f) {
  var k, result, v;
  result = {};
  for (k in obj) {
    v = obj[k];
    result[k] = f(v);
  }
  return result;
};
joyrexus
fonte
2
const mapObject = (obj = {}, mapper) =>
  Object.entries(obj).reduce(
    (acc, [key, val]) => ({ ...acc, [key]: mapper(val) }),
    {},
  );
Nigel Kirby
fonte
Eu estava prestes a adicionar esta resposta eu mesmo. Ainda bem que eu rolei!
Bill Criswell
0

Uma correção de mix para o bug de sublinhado do mapa : P

_.mixin({ 
    mapobj : function( obj, iteratee, context ) {
        if (obj == null) return [];
        iteratee = _.iteratee(iteratee, context);
        var keys = obj.length !== +obj.length && _.keys(obj),
            length = (keys || obj).length,
            results = {},
            currentKey;
        for (var index = 0; index < length; index++) {
          currentKey = keys ? keys[index] : index;
          results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
        }
        if ( _.isObject( obj ) ) {
            return _.object( results ) ;
        } 
        return results;
    }
}); 

Uma solução simples que mantém a tecla correta e retorna como objeto. Ainda é usada da mesma maneira que eu convidava. Você poderia usar essa função para substituir a função _.map.

ou simplesmente como eu o usei como um mixin

_.mapobj ( options , function( val, key, list ) 
Pascal
fonte