функция карты для объектов (вместо массивов)

723

У меня есть объект:

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

Я ищу собственный метод, похожий на Array.prototype.map, который будет использоваться следующим образом:

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

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

Есть ли у JavaScript такая функция map для объектов? (Я хочу это для Node.JS, поэтому мне не нужны проблемы с перекрестным браузером.)

  • 1
    В большинстве ответов используется Object.keys , который не имеет четко определенного порядка. Это может быть проблематично, я предлагаю вместо этого использовать Object.getOwnPropertyNames .
  • 3
    Удивительно для меня, что JS не предусмотрел эту невероятно рутинную задачу.
Показать ещё 3 комментария
Теги:
functional-programming
map-function

28 ответов

1002
Лучший ответ

Там нет собственной map Object, но как насчет этого:

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

Object.keys(myObject).map(function(key, index) {
  myObject[key] *= 2;
});

console.log(myObject);
// => { 'a': 2, 'b': 4, 'c': 6 }

Но вы можете легко перебрать объект, используя for... in:

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

for (var key in myObject) {
  if (myObject.hasOwnProperty(key)) {
    myObject[key] *= 2;
  }
}

console.log(myObject);
// { 'a': 2, 'b': 4, 'c': 6 }

Обновить

Многие люди упоминают, что предыдущие методы не возвращают новый объект, а работают с самим объектом. В связи с этим я хотел добавить другое решение, которое возвращает новый объект и оставляет исходный объект как есть:

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

// returns a new object with the values at each key mapped using mapFn(value)
function objectMap(object, mapFn) {
  return Object.keys(object).reduce(function(result, key) {
    result[key] = mapFn(object[key])
    return result
  }, {})
}

var newObject = objectMap(myObject, function(value) {
  return value * 2
})

console.log(newObject);
// => { 'a': 1, 'b': 4, 'c': 6 }

console.log(myObject);
// => { 'a': 1, 'b': 2, 'c': 3 }

Array.prototype.reduce сводит массив к одному значению, несколько объединяя предыдущее значение с текущим. Цепочка инициализируется пустым объектом {}. На каждой итерации добавляется новый ключ myObject с его квадратом в качестве значения.

  • 0
    он также не сохраняет семантику Array.map , который должен возвращать массив.
  • 3
    Да, я бы не рекомендовал свое решение для такого рода проблем. Вот почему я обновил свой ответ альтернативным, лучшим решением.
Показать ещё 18 комментариев
169

Как насчет одного вкладыша с немедленным назначением переменной в простом JS (ES6/ES2015)?

Использование оператора распространения и синтаксиса имени вычисляемого ключа:

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

jsbin

Другая версия, использующая Reduce:

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

jsbin

Первый пример как функция:

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

Если вы хотите отобразить вложенный объект рекурсивно в функциональном стиле, это можно сделать так:

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

Или более обязательно, как это:

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

Начиная с ES7/ES2016 вы можете использовать Object.entries вместо Object.keys например, так:

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


Унаследованные свойства и прототип цепочки:

В некоторых редких случаях вам может понадобиться отобразить объект класса, который содержит свойства унаследованного объекта в его цепочке прототипов. В таких случаях Object.keys() не будет работать, потому что Object.keys() не перечисляет унаследованные свойства. Если вам нужно отобразить унаследованные свойства, вы должны использовать for (key in myObj) {...}.

Вот пример объекта, который наследует свойства другого объекта, и как Object.keys() не работает в таком сценарии.

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

Однако, пожалуйста, сделайте мне одолжение и избегайте наследства. :-)

  • 1
    Красиво, но я поймал тот факт, что Object.keys не перечисляет унаследованные свойства. Я предлагаю вам добавить предупреждение.
  • 0
    Рад ответить (ы) на истекающих кровью кромок, я переключил nvm на 7+ и его масло.
Показать ещё 9 комментариев
91

Нет встроенных методов, но lodash # mapValues ​​ будет выполнять работу блестяще

_.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
// → { 'a': 3, 'b': 6, 'c': 9 }
  • 4
    Нет необходимости добавлять дополнительный HTTP-вызов и дополнительную библиотеку только для одной функции. В любом случае, этот ответ устарел, и вы можете просто вызвать Object.entries({a: 1, b: 2, c: 3}) чтобы получить массив.
  • 4
    Почему получение массива полезно? Требование было для сопоставленного объекта. Также не подразумевается HTTP-вызов при использовании Lodash, популярной библиотеки, специально созданной для такого рода вещей, хотя есть довольно много вещей, которые ES6 исключил необходимость использования библиотек служебных функций в последнее время.
Показать ещё 2 комментария
57

Очень легко написать один:

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;
}

с примером кода:

> 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: эта версия также позволяет (необязательно) установить контекст this для обратного вызова, как и метод Array.

EDIT - изменено, чтобы удалить использование Object.prototype, чтобы убедиться, что он не сталкивается с любым существующим свойством с именем map на объекте.

  • 0
    downvoter - пожалуйста, объясните!
  • 0
    (Не downvoter) Где c взялось? Это была ошибка копирования / вставки?
Показать ещё 15 комментариев
21

Вы можете использовать Object.keys, а затем forEach по возвращенному массиву ключей:

var myObject = { 'a': 1, 'b': 2, 'c': 3 },
    newObject = {};
Object.keys(myObject).forEach(function (key) {
    var value = myObject[key];
    newObject[key] = value * value;
});

Или более модульным способом:

function map(obj, callback) {
    var result = {};
    Object.keys(obj).forEach(function (key) {
        result[key] = callback.call(obj, obj[key], key, obj);
    });
    return result;
}

newObject = map(myObject, function(x) { return x * x; });

Обратите внимание, что Object.keys возвращает массив, содержащий только собственные свойства перечисления объекта, поэтому он ведет себя как цикл for..in с проверкой hasOwnProperty.

14

Это прям бразильцы, и все в сообществе JS знают это. Должна быть эта функциональность:

const obj1 = {a:4, b:7};
const obj2 = Object.map(obj1, (k,v) => v + 5);

console.log(obj1); // {a:4, b:7}
console.log(obj2); // {a:9, b:12}

вот наивная реализация:

Object.map = function(obj, fn, ctx){

    const ret = {};

    Object.keys(obj).forEach(function(k){
        ret[k] = fn.call(ctx || null, k, obj[k]);
    });

    return ret;
};

это очень раздражает, когда приходится все время реализовывать самостоятельно;)

Если вы хотите что-то более сложное, которое не мешает классу Object, попробуйте это:

let map = function (obj, fn, ctx) {
  return Object.keys(obj).reduce((a, b) => {
    a[b] = fn.call(ctx || null, b, obj[b]);
    return a;
  }, {});
};


const x = map({a: 2, b: 4}, (k,v) => {
    return v*2;
});

но безопасно добавить эту функцию карты в Object, просто не добавляйте в Object.prototype.

Object.map = ... // fairly safe
Object.prototype.map ... // not ok
  • 0
    Почему вы добавляете эту функцию к глобальному объекту Object ? Просто используйте const mapObject = (obj, fn) => { [...]; return ret; }
  • 6
    Потому что это должен быть метод на объекте :)
Показать ещё 14 комментариев
12

Я пришел сюда, чтобы найти и ответить за сопоставление объекта с массивом и получить эту страницу в результате. В случае, если вы пришли сюда, ища тот же ответ, что и я, вот как вы можете сопоставить и возразить массиву.

Вы можете использовать карту для возврата нового массива из объекта следующим образом:

var newObject = Object.keys(myObject).map(function(key) {
   return myObject[key];
});
  • 4
    Это не работает удаленно - он просто возвращает массив значений объекта, а не новый объект. Я не могу поверить, что у него так много голосов.
  • 0
    Вы правы, он не отвечает на вопрос правильно, так как они искали ответ для сопоставления объекта и объекта. Я пришел сюда в поисках ответа для отображения объекта в массив и получил эту страницу в результате, поэтому я оставлю свой ответ и отредактирую его, чтобы отразить, что это альтернативный ответ для людей, которые, возможно, попали сюда в результате ищет отображение объекта в массивах.
Показать ещё 4 комментария
11

Принятый ответ имеет два недостатка:

  • Он злоупотребляет Array.prototype.reduce, потому что сокращение означает изменение структуры составного типа, чего в этом случае не происходит.
  • Это не особенно многократно используется

Функциональный подход ES6/ES2015

Обратите внимание, что все функции определены в карри.

// 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));
  • В строке A o переназначается. Поскольку Javascript передает ссылочные значения путем совместного использования, создается неглубокая копия o. Теперь мы можем мутировать o внутри omap без мутации o в родительской области.
  • В строке B map возвращаемое значение игнорируется, потому что map выполняет мутацию o. Поскольку этот побочный эффект остается в пределах omap и не отображается в родительской области, это вполне приемлемо.

Это не самое быстрое решение, но декларативное и многоразовое. Вот такая же реализация, как однострочная, краткая, но менее читаемая:

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

Добавление - почему объекты по умолчанию не повторяются?

ES2015 указал итератор и итерационные протоколы. Но объекты по-прежнему не повторяются и, следовательно, не отображаются. Причина заключается в смешении уровня данных и программ.

  • 1
    Похоже, немного перегружен; Я считаю 6 функций для того, что по сути является циклом. Другие решения намного проще, и принятый ответ также в 5 раз быстрее.
  • 4
    @ bfred.it Какова цель вашего комментария? Если вам нравится Array.prototype.reduce вы также не должны использовать Array.prototype.reduce . Все, что я хочу проиллюстрировать здесь, это то, что принятый ответ как-то «злоупотребляет» Array.prototype.reduce и как может быть реализовано чисто функциональное отображение. Если вы не заинтересованы в функциональном программировании, просто проигнорируйте мой ответ.
Показать ещё 10 комментариев
8

Для максимальной производительности.

Если ваш объект не изменяется часто, но его нужно повторять часто, я предлагаю использовать собственную карту в качестве кеша.

// example object
var obj = {a: 1, b: 2, c: 'something'};

// caching map
var objMap = new Map(Object.entries(obj));

// fast iteration on Map object
objMap.forEach((item, key) => {
  // do something with an item
  console.log(key, item);
});

Object.entries уже работает в Chrome, Edge, Firefox и бета-версии Opera, так что это надежная функция. Это от ES7, поэтому polyfill it https://github.com/es-shims/Object.entries для IE, где он не работает.

  • 0
    Как насчет использования Разрушения ?
  • 1
    @ K._ Раньше это было очень медленно, но я не знаю о производительности сейчас. Похоже, что V8 v 5.6 представил некоторые оптимизации для деструктуризации, но это должно быть измерено v8project.blogspot.co.uk
Показать ещё 2 комментария
7

Минимальная версия (es6):

Object.entries(obj).reduce((a, [k, v]) => (a[k] = v * v, a), {})
  • 0
    Я думаю, что это неправильно из-за опечатки, я думаю, что вы имеете в виду => Object.entries(obj).reduce((a, [k, v]) => [a[k] = v * v, a], {}) , угловые скобки вместо скобок.
  • 0
    @AlexanderMills, мой код правильный. Узнайте больше об операторе запятой в скобках: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Показать ещё 4 комментария
6

Этот ответ был переписан, чтобы продемонстрировать мощный комбинатор mapReduce который позволяет нам думать о нашем преобразовании по-другому

  1. m, функция отображения - дает вам возможность преобразовать входящий элемент, прежде чем...
  2. r, функция сокращения - эта функция объединяет аккумулятор с результатом сопоставленного элемента

Интуитивно mapReduce, что mapReduce создает новый редуктор, который мы можем подключить непосредственно к Array.prototype.reduce. Но что еще более важно, мы можем реализовать нашу реализацию объектного функтора omap просто, используя объект Object.assign, Object.assign и {}.

const identity = x =>
  x
  
const first = (a, b) =>
  a
  
const mapReduce = (m = identity, r = first) =>
  (acc, x) => r (acc, m (x))
  
const omap = (o = {}, f = identity) =>
  Object.keys (o)
    .reduce ( mapReduce ( k => ({ [k]: f (o [k]) })  // Object.map
                        , Object.assign              // Object.concat
                        )
            , {}                                     // Object.empty
            )
          
const square = x =>
  x * x
  
const data =
  { a : 1, b : 2, c : 3 }
  
console.log (omap (data, square))
// { a : 1, b : 4, c : 9 }

Обратите внимание, что единственная часть программы, которую мы фактически должны были написать, - это сама реализация отображения.

k => ({ [k]: f (o [k]) }

В котором говорится, что для заданного известного объекта o и некоторого ключа k создается объект, свойство которого k является результатом вызова f для значения ключа o [k].

Мы получим представление о mapReduce секвенирования mapReduce если сначала oreduce абстрактный

const oreduce = (o, f = first, acc = {}) =>
  Object.keys (o)
    .reduce ( mapReduce ( k => [ k, o[k] ]
                        , f
                        )
            , acc
            )

const omap = (o = {}, f = identity) =>
  oreduce ( o
          , mapReduce ( ([ k, v ]) => ({ [k]: f (v) })
                      , Object.assign
                      )
          , {}
          )

Все работает одинаково, но omap теперь можно определить на более высоком уровне. Конечно, новый Object.entries выглядит глупо, но упражнение все еще важно для ученика.

Вы не увидите весь потенциал mapReduce здесь, но я поделюсь этим ответом, потому что интересно посмотреть, сколько мест его можно применить. Если вы заинтересованы в том, как это происходит и как это может быть полезно, пожалуйста, посмотрите этот ответ.

  • 3
    Давайте лучше использовать Map и Map.entries , хорошо: D. Я устал от использования простых объектов в качестве типов картографических данных.
  • 2
    Я согласен, что Map следует использовать там, где это возможно, но это не полностью заменяет необходимость использования этих процедур на простых объектах JS, хотя: D
5

Вы можете использовать метод map и forEach для массивов, но если вы хотите использовать его для Object вы можете использовать его с твистом, как показано ниже:

Использование Javascript (ES6)

var obj = { 'a': 2, 'b': 4, 'c': 6 };   
Object.entries(obj).map( v => obj[v[0]] *= v[1] );
console.log(obj); //it will log as {a: 4, b: 16, c: 36}

var obj2 = { 'a': 4, 'b': 8, 'c': 10 };
Object.entries(obj2).forEach( v => obj2[v[0]] *= v[1] );
console.log(obj2); //it will log as {a: 16, b: 64, c: 100}

Использование jQuery

var ob = { 'a': 2, 'b': 4, 'c': 6 };
$.map(ob, function (val, key) {
   ob[key] *= val;
});
console.log(ob) //it will log as {a: 4, b: 16, c: 36}

Или вы можете использовать другие циклы, такие как метод $.each как $.each ниже:

$.each(ob,function (key, value) {
  ob[key] *= value;
});
console.log(ob) //it will also log as {a: 4, b: 16, c: 36}
  • 0
    Где $ это jquery?
  • 0
    Да, вы можете использовать как $, так и JQuery
Показать ещё 3 комментария
3

map function не существует на Object.prototype, однако вы можете эмулировать его так:

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;
});
  • 0
    Проблемы производительности: typeof callback === 'function' абсолютно избыточен. 1) map не имеет смысла без обратного вызова 2) если callback не является функцией, ваша реализация map просто выполняет бессмысленный цикл for. Кроме того, Object.prototype.hasOwnProperty.call( obj, key ) немного перебор.
2

Я натолкнулся на это как на первый элемент в поиске Google, пытаясь научиться делать это, и подумал, что поделюсь с другими людьми, которые недавно нашли это решение, которое я нашел, использующее неизменяемый пакет npm.

Я думаю, что им интересно поделиться, потому что immutable использует ситуацию OP EXACT в своей собственной документации - следующее не мой собственный код, а взятый из текущей документации immutable-js:

const { Seq } = require('immutable')
const myObject = { a: 1, b: 2, c: 3 }
Seq(myObject).map(x => x * x).toObject();
// { a: 1, b: 4, c: 9 } 

Не то, чтобы у Seq были другие свойства ("Seq описывает ленивую операцию, позволяющую им эффективно объединять в цепочку использование всех методов сбора данных более высокого порядка (таких как map и filter), не создавая промежуточные коллекции"), и что некоторые другие данные immutable-js Структуры также могут выполнять работу достаточно эффективно.

Любой, кто использует этот метод, конечно, должен npm install immutable и может захотеть прочитать документы:

https://facebook.github.io/immutable-js/

2
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 }
2

На основании ответа @Amberlamps, здесь функция полезности (как комментарий выглядел уродливым)

function mapObject(obj, mapFunc){
    return Object.keys(obj).reduce(function(newObj, value) {
        newObj[value] = mapFunc(obj[value]);
        return newObj;
    }, {});
}

и используется:

var obj = {a:1, b:3, c:5}
function double(x){return x * 2}

var newObj = mapObject(obj, double);
//=>  {a: 2, b: 6, c: 10}
1

Мой ответ в значительной степени основан на ответе с самым высоким рейтингом здесь, и, надеюсь, все понимают (тоже самое объяснение на моем GitHub). Вот почему его имплементация с картой работает:

Object.keys(images).map((key) => images[key] = 'url(' + '"' + images[key] + '"' +    
')');

Назначение функции - взять объект и изменить исходное содержимое объекта, используя метод, доступный для всех объектов (как объектов, так и массивов) без возврата массива. Почти все в JS является объектом, и по этой причине элементы, расположенные ниже по конвейеру наследования, могут технически использовать те, которые доступны для тех, кто находится на линии вверх (и наоборот).

Это работает из-за того, что функции .map возвращают массив, ТРЕБУЮЩИЙ, что вы предоставляете явный или неявный ВОЗВРАТ массива вместо простой модификации существующего объекта. По сути, вы заставляете программу думать, что объект является массивом, используя Object.keys, который позволит вам использовать функцию map, воздействуя на значения, с которыми связаны отдельные ключи (я фактически случайно возвратил массивы, но исправил это). Пока нет возврата в обычном смысле, не будет никакого массива, созданного с оригинальным объектом, все еще целым и измененным как запрограммированный.

Эта конкретная программа принимает объект с именем images, принимает значения его ключей и добавляет теги url для использования в другой функции. Оригинал это:

var images = { 
snow: 'https://www.trbimg.com/img-5aa059f5/turbine/bs-md-weather-20180305', 
sunny: 'http://www.cubaweather.org/images/weather-photos/large/Sunny-morning-east-   
Matanzas-city- Cuba-20170131-1080.jpg', 
rain: 'https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg' };

... и модифицировано это:

var images = { 
snow: url('https://www.trbimg.com/img-5aa059f5/turbine/bs-md-weather-20180305'),     
sunny: url('http://www.cubaweather.org/images/weather-photos/large/Sunny-morning-   
east-Matanzas-city- Cuba-20170131-1080.jpg'), 
rain: url('https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg') 
};

Исходная структура объекта остается неизменной, что обеспечивает нормальный доступ к свойству, пока нет возврата. НЕ возвращайте массив, как обычно, и все будет хорошо. Цель - переназначить исходные значения (изображения [ключ]) на то, что нужно, а не на что-либо еще. Насколько я знаю, для предотвращения вывода массива должна быть ПЕРЕДАЧА изображений [ключ] и неявный или неявный запрос на возврат массива (назначение переменных делает это и дает сбои назад и вперед для меня).

РЕДАКТИРОВАТЬ:

Собираюсь обратиться к своему другому методу в отношении создания нового объекта, чтобы избежать изменения исходного объекта (и переназначение, по-видимому, все еще необходимо, чтобы избежать случайного создания массива в качестве вывода). Эти функции используют синтаксис стрелок и, если вы просто хотите создать новый объект для будущего использования.

const mapper = (obj, mapFn) => Object.keys(obj).reduce((result, key) => {
                result[key] = mapFn(obj)[key];
                return result;
            }, {});

var newImages = mapper(images, (value) => value);

Принцип работы этих функций выглядит примерно так:

mapFn берет функцию, которая будет добавлена позже (в этом случае (value) => value), и просто возвращает все, что там хранится, в качестве значения для этого ключа (или умножается на два, если вы измените возвращаемое значение, как он сделал) в mapFn ( объект) [ключ],

а затем переопределяет исходное значение, связанное с ключом, в результате [ключ] = mapFn (obj) [ключ]

и возвращает операцию, выполненную с результатом (аккумулятор, расположенный в скобках, инициированный в конце функции .reduce).

Все это выполняется на выбранном объекте, и ЕЩЕ НЕ МОЖЕТ быть неявный запрос возвращаемого массива, и он работает только при переназначении значений, насколько я могу судить. Это требует некоторой умственной гимнастики, но сокращает количество необходимых строк кода, как видно выше. Вывод точно такой же, как видно ниже:

{snow: "https://www.trbimg.com/img-5aa059f5/turbine/bs-   
md-weather-20180305", sunny: "http://www.cubaweather.org/images/weather-
photos/l…morning-east-Matanzas-city-Cuba-20170131-1080.jpg", rain: 
"https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg"}

Имейте в виду, что это работало с НОМЕРАМИ. Вы МОЖЕТЕ Дублировать ЛЮБОЙ объект, ПРОСТО ВОЗВРАЩАЯ ЗНАЧЕНИЕ в функции mapFN.

1

Сначала преобразуйте свою коллекцию HTMLCollection, используя Object.entries(collection). Тогда это итеративно, теперь вы можете использовать метод .map для него.

Object.entries(collection).map(...)

ссылка https://medium.com/@js_tut/calling-javascript-code-on-multiple-div-elements-without-the-id-attribute-97ff6a50f31

1

Чтобы более точно реагировать на то, что именно запрашивал ОП, ОП хочет объект:

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

иметь метод карты myObject.map,

похож на Array.prototype.map, который будет использоваться следующим образом:

newObject = myObject.map(function (value, label) {
    return value * value;
});
// newObject is now { 'a': 1, 'b': 4, 'c': 9 }

Лучший ответ imho (измеренный в терминах "близко к тому, что спрашивается" + "нет необходимости в ES {5,6,7} без необходимости") будет следующим:

myObject.map = function mapForObject(callback)
{
  var result = {};
  for(var property in this){
    if(this.hasOwnProperty(property) && property != "map"){
      result[property] = callback(this[property],property,this);
    }
  }
  return result;
}

Приведенный выше код избегает намеренного использования каких-либо языковых функций, доступных только в последних выпусках ECMAScript. С помощью приведенного выше кода проблема может быть решена следующим образом:

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

myObject.map = function mapForObject(callback)
{
  var result = {};
  for(var property in this){
    if(this.hasOwnProperty(property) && property != "map"){
      result[property] = callback(this[property],property,this);
    }
  }
  return result;
}

newObject = myObject.map(function (value, label) {
  return value * value;
});
console.log("newObject is now",newObject);
альтернативный тестовый код здесь

Помимо того, что некоторые нахмурились, была бы возможность вставить решение в цепочку прототипов следующим образом.

Object.prototype.map = function(callback)
{
  var result = {};
  for(var property in this){
    if(this.hasOwnProperty(property)){
      result[property] = callback(this[property],property,this);
    }
  }
  return result;
}

Что-то, что при тщательном надзоре не должно иметь побочных эффектов и не влиять на метод map других объектов (например, map массива).

1

Вы можете использовать простой цикл for через упорядоченные пары. Я использовал hasOwnProperty() потому что вы создали три свойства (со значениями) для вашего объекта. Первый метод не создает карту. Вместо этого он просто применяет функцию к одному элементу, что может значительно ускорить выполнение во многих ситуациях.

Второй метод создает карту таким же образом, как и первый, но, вероятно, довольно медленный, чем некоторые другие ответы здесь.

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>
  • 2
    Это не отвечает на первоначальный вопрос - оно не отображает значения в новый объект с предоставленной функцией (то есть это не похоже на Array.prototype.map).
  • 0
    @MattBrowne Я добавил второй метод, который создает, возможно, карту. Это медленно, но делает то, что запрашивает OP. Первый метод идеален во многих ситуациях, поэтому я использовал цикл и функции свойств.
1

Я специально хотел использовать ту же функцию, которую я использовал для массивов для одного объекта, и хотел, чтобы это было просто. Это сработало для меня:

var mapped = [item].map(myMapFunction).pop();
1

Мне нужна была версия, которая также позволяла изменять ключи (на основе ответов @Amberlamps и @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"}
}

Изменить: небольшое изменение для перехода в исходный объект {}. Позволяет ему быть [] (если ключи целые)

1

Эй написал небольшую функцию mapper, которая могла бы помочь.

    function propertyMapper(object, src){
         for (var property in object) {   
           for (var sourceProp in src) {
               if(property === sourceProp){
                 if(Object.prototype.toString.call( property ) === '[object Array]'){
                   propertyMapper(object[property], src[sourceProp]);
                   }else{
                   object[property] = src[sourceProp];
                }
              }
            }
         }
      }
  • 1
    Я полагаю, вы имели в виду "я" вместо "Эй"
1

Если вас интересует map ping не только значения, но и ключи, я написал Object.map(valueMapper, keyMapper), который ведет себя таким образом

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 }
  • 1
    Есть ли причина для понижения?
  • 3
    Я полагаю, потому что вы в основном только что предоставили ссылку в качестве ответа, который осуждается.
Показать ещё 1 комментарий
0

Вы можете использовать прототип Array и функцию call() для отображения объекта

Array.prototype.map.call(Obj, function(currentItem, index){
 /** Your code... */ 
})

По сути, вы вызываете функцию map, которая лежит в Array.prototype, и с помощью call (Obj, fn) вы говорите javascript использовать Obj как "массив".

Вы также можете избежать "Array.prototype" и использовать вместо него пустой массив, он имеет тот же эффект

[].map.call(Obj, function(currentItem, index){
 /** Your code... */ 
})
0

Если кто-то искал простое решение, которое отображает объект на новый объект или массив:

// Maps an object to a new object by applying a function to each key+value pair.
// Takes the object to map and a function from (key, value) to mapped value.
const mapObject = (obj, fn) => {
    const newObj = {};
    Object.keys(obj).forEach(k => { newObj[k] = fn(k, obj[k]); });
    return newObj;
};

// Maps an object to a new array by applying a function to each key+value pair.
// Takes the object to map and a function from (key, value) to mapped value.
const mapObjectToArray = (obj, fn) => (
    Object.keys(obj).map(k => fn(k, obj[k]))
);

Это может не работать для всех объектов или всех функций отображения, но это работает для простых мелких объектов и простых функций отображения, и это все, что мне нужно.

-1

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>

Используйте новый _.transform() для преобразования объекта.

-2

Другой подход заключается в использовании пользовательской функции json stringify, которая также может работать с глубокими объектами. Это может быть полезно, если вы все равно собираетесь отправить его на сервер как json

const obj = { 'a': 1, 'b': 2, x: {'c': 3 }}
const json = JSON.stringify(obj, (k, v) => typeof v === 'number' ? v * v : v)

console.log(json)
console.log('back to json:', JSON.parse(json))

Ещё вопросы

Сообщество Overcoder
Наверх
Меню