funkcja mapy dla obiektów (zamiast tablic)

1064

Mam przedmiot:

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

Szukam metody natywnej, podobnej do Array.prototype.maptej, która byłaby używana w następujący sposób:

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

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

Czy JavaScript ma taką mapfunkcję dla obiektów? (Chcę tego dla Node.JS, więc nie dbam o problemy z przeglądarkami).

Randomblue
źródło
1
Wykorzystuje większość odpowiedzi Object.keys, które nie mają dobrze określonej kolejności. Może to być problematyczne, sugeruję użycie Object.getOwnPropertyNameszamiast tego.
Oriol,
14
Zdumiewające dla mnie, że JS nie przewidziało tego niezwykle podstawowego zadania.
jchook
3
@Oriol, jesteś tego pewien? Według dokumentów internetowych MDN kolejność elementów tablicy jest spójna między Object.keysi Object.getOwnPropertyNames. Zobacz developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Bart
@Bart Kolejność Object.keyszależy od implementacji. Zobacz stackoverflow.com/a/30919039/1529630
Oriol
4
@Oriol I tak nie powinieneś polegać na kolejności kluczy w przedmiotach, więc jest to trochę nieistotne dla pytania - ponieważ nigdy nie określił, że kolejność ma dla niego znaczenie. A jeśli porządek miał dla niego znaczenie, to i tak nie powinien używać przedmiotu.
BT

Odpowiedzi:

1540

Nie ma rodzimy mapsię do Objectobiektu, ale jak o tym:

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 }

Ale możesz łatwo iterować obiekt, używając 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 }

Aktualizacja

Wiele osób wspomina, że ​​poprzednie metody nie zwracają nowego obiektu, a raczej działają na samym obiekcie. W związku z tym chciałem dodać inne rozwiązanie, które zwraca nowy obiekt i pozostawia oryginalny obiekt bez zmian:

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': 2, 'b': 4, 'c': 6 }

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

Array.prototype.reduceredukuje tablicę do pojedynczej wartości, nieco łącząc poprzednią wartość z bieżącą. Łańcuch jest inicjowany przez pusty obiekt {}. Przy każdej iteracji nowy kluczmyObject dodawany jest z podwójnym kluczem jako wartością.

Aktualizacja

Dzięki nowym funkcjom ES6 istnieje bardziej elegancki sposób wyrażania objectMap.

const objectMap = (obj, fn) =>
  Object.fromEntries(
    Object.entries(obj).map(
      ([k, v], i) => [k, fn(v, k, i)]
    )
  )
  
const myObject = { a: 1, b: 2, c: 3 }

console.log(objectMap(myObject, v => 2 * v)) 

Amberlampy
źródło
3
Tak, nie poleciłbym mojego rozwiązania tego rodzaju problemu. Dlatego zaktualizowałem swoją odpowiedź alternatywnym, lepszym rozwiązaniem.
Amberlamps,
1
Zaktualizowałem swoją odpowiedź - wydaje się (po ponownym przeczytaniu pytania), że OP chce nowego obiektu z tymi samymi kluczami, w którym wartości zostały zmutowane za pomocą dostarczonej funkcji, a nie modyfikować oryginalnego obiektu na miejscu.
Alnitak,
13
@KhalilRavanna Myślę, że źle odczytałeś tutaj kod - ta odpowiedź nie jest używana mappoprawnie, ponieważ nie robi return- jest nadużywająca, mapjakby była forEachrozmową. Gdyby rzeczywiście tak zrobił, return myObject[value] * 2 to wynikiem byłaby tablica zawierająca podwojone oryginalne wartości, a nie obiekt zawierający oryginalne klucze z podwójnymi wartościami, przy czym te ostatnie byłyby wyraźnie tym, o co poprosił OP.
Alnitak
3
@Amberlamps twój .reduceprzykład jest OK, ale IMHO wciąż jest daleko w tyle za wygodą a .mapdla Objects, ponieważ twoje .reducewywołanie zwrotne musi nie tylko pasować do tego .reduce, co działa, ale także wymaga, aby myObjectbyły dostępne w zakresie leksykalnym. To ostatnie w szczególności uniemożliwia przekazanie odwołania do funkcji w wywołaniu zwrotnym, wymagając zamiast tego funkcji anonimowej w miejscu.
Alnitak
8
TBH Wolałbym raczej głosować na odpowiedź, która tylko (lub pierwotnie) stanowiła drugie rozwiązanie przedstawione w tej odpowiedzi. Pierwszy działa i niekoniecznie jest zły, ale jak powiedzieli inni, nie lubię, aby mapa była używana w ten sposób. Kiedy widzę mapę, mój umysł automatycznie myśli o „niezmiennej strukturze danych”
mjohnsonengr
307

Co powiesz na jedną linijkę z natychmiastowym przypisywaniem zmiennych w zwykłym JS ( ES6 / ES2015 )?

Wykorzystanie operatora rozkładania i obliczonej składni nazwy klucza :

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

jsbin

Inna wersja używająca redukcji:

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

jsbin

Pierwszy przykład jako funkcja:

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

Jeśli chcesz odwzorować zagnieżdżony obiekt rekurencyjnie w funkcjonalnym stylu, możesz to zrobić w następujący sposób:

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

Lub bardziej koniecznie:

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

Od wersji ES7 / ES2016 możesz używać Object.entries()zamiast Object.keys()np .:

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

Wprowadzono ES2019Object.fromEntries() , który jeszcze bardziej upraszcza:

let newObj = Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, v * v]));


Dziedziczone właściwości i łańcuch prototypów:

W niektórych rzadkich sytuacjach może być konieczne odwzorowanie obiektu podobnego do klasy, który posiada właściwości odziedziczonego obiektu w łańcuchu prototypów . W takich przypadkach Object.keys()nie będzie działać, ponieważ Object.keys()nie wylicza odziedziczonych właściwości. Jeśli potrzebujesz mapować odziedziczone właściwości, powinieneś użyćfor (key in myObj) {...} .

Oto przykład obiektu, który dziedziczy właściwości innego obiektu i jak Object.keys()nie działa w takim scenariuszu.

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

Wyświadcz mi jednak przysługę i unikaj dziedziczenia . :-)

Rotareti
źródło
1
Piękne, ale złapał mnie fakt, że Object.keysnie wylicza odziedziczonych właściwości. Sugeruję dodanie ostrzeżenia.
David Braun
Aby skrócić swoją odpowiedź, po prostu użyj Object.entries({a: 1, b: 2, c: 3}).
1
Masz kilka literówek ( (o, f)obj
określasz
4
Tak, a następnie pozwól następnej osobie poświęcić kilka minut na zastanowienie się, na czym polega ta funkcja? :)
Andrey Popov,
1
Rozwiązanie Object.assign(...Object.entries(obj).map(([k, v]) => ({[k]: v * v})))nie działa, jeśli objjest pustym obiektem. Przejdź do: Object.assign({}, ...Object.entries(obj).map(([k, v]) => ({[k]: v * v}))).
blackcatweb,
115

Brak metod natywnych, ale lodash # mapValues wykona zadanie doskonale

_.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
// → { 'a': 3, 'b': 6, 'c': 9 }
Mario
źródło
6
Nie ma potrzeby dodawania dodatkowego wywołania HTTP i dodatkowej biblioteki tylko dla jednej funkcji. W każdym razie ta odpowiedź jest teraz nieaktualna i możesz po prostu zadzwonić, Object.entries({a: 1, b: 2, c: 3})aby uzyskać tablicę.
10
Dlaczego warto uzyskać tablicę? Wymagany był obiekt odwzorowany. Nie ma też domniemanych wywołań HTTP przy użyciu Lodash, popularnej biblioteki zasadniczo zbudowanej dla tego typu rzeczy, chociaż jest kilka rzeczy, które ES6 wyklucza ostatnio konieczność korzystania z bibliotek funkcji narzędziowych.
corse32
2
dodatkowe połączenie HTTP do ściągnięcia Lodasha, tak myślę. Jeśli jest to twój jedyny przypadek użycia, rzeczywiście jest to przesada. Niemniej jednak warto mieć tę odpowiedź, ponieważ Lodash jest tak rozpowszechnioną biblioteką.
igorsantos07
2
nie wspominając o tym, że powinieneś faktycznie użyć, _.map()aby otrzymać klucz jako drugi argument - zgodnie z wymaganiami PO.
igorsantos07
57

Łatwo jest napisać jeden:

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

z przykładowym kodem:

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

Uwaga: ta wersja pozwala również (opcjonalnie) ustawić thiskontekst dla wywołania zwrotnego, podobnie jak Arraymetoda.

EDYCJA - zmieniono, aby usunąć użycie Object.prototype, aby upewnić się, że nie koliduje z żadną istniejącą właściwością nazwaną mapna obiekcie.

Alnitak
źródło
3
Ok. :) Sprowadził mnie tutaj komentarz do tego pytania . Daję +1, ponieważ zaakceptowana odpowiedź jest wyraźnie niewłaściwie wykorzystywana map, ale muszę powiedzieć, że modyfikowanie Object.prototypenie pasuje do mnie, nawet jeśli nie jest policzalne.
JLRishe
2
@JLRishe Thanks. IMHO w ES5 tak naprawdę nie ma już żadnego powodu, aby nie modyfikować Object.prototypeinnego niż (teoretyczne) ryzyko kolizji z innymi metodami. FWIW, nie rozumiem, w jaki sposób druga odpowiedź zdobyła tyle głosów, ile ma, jest całkowicie błędna.
Alnitak
7
@JLRishe, jesteś starym geezerem ... zmień czas i zmodyfikuj ten prototyp!
Nick Manning
12
@NickManning You Young Whippersnappers i twoje nieodpowiedzialne zachowanie. Wynoś się z mojego trawnika!
JLRishe,
3
To już nie działa: o = {map: true, image: false}. Jesteś narażając go po prostu napisać o.map(fn)zamiastmap(o,fn)
fregante
25

Możesz użyć, Object.keysa następnie forEachnad zwróconą tablicą kluczy:

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

Lub w bardziej modułowy sposób:

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

Zauważ, że Object.keyszwraca tablicę zawierającą tylko własne wyliczalne właściwości obiektu, dlatego zachowuje się jak for..inpętla z hasOwnPropertyczekiem.

Mattias Buelens
źródło
20

To proste bs i wszyscy w społeczności JS o tym wiedzą. Nie powinno być to funkcjonalność:

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}

oto naiwne wdrożenie:

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

    const ret = {};

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

    return ret;
};

to bardzo denerwujące, że trzeba to cały czas wdrażać;)

Jeśli chcesz czegoś bardziej zaawansowanego, co nie koliduje z klasą Object, spróbuj tego:

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

ale można bezpiecznie dodać tę funkcję mapy do Object, po prostu nie dodawaj do Object.prototype.

Object.map = ... // fairly safe
Object.prototype.map ... // not ok
Alexander Mills
źródło
Dlaczego dodajesz tę funkcję do Objectobiektu globalnego ? Wystarczy const mapObject = (obj, fn) => { [...]; return ret; }.
Amberlamps
7
Ponieważ powinna to być metoda na Object :)
Alexander Mills
1
Tak długo, jak nie zadzieramy z Object.prototype, powinniśmy być w porządku
Alexander Mills
1
Oczywiście, jeśli mapa była przypadkową, niejasną rzeczą. Ale .mapjest ogólnie rozumiany jako interfejs Functor i nie ma jednej definicji Functora dla „Object”, ponieważ praktycznie wszystko w javascript może być Object, w tym rzeczy z własnymi bardzo wyraźnymi i zdefiniowanymi interfejsami / logiką .map.
Dtipson
1
Moim zdaniem pierwszym argumentem wywołania zwrotnego powinien być iterowany element, a nie klucz. Na przykład zakładam, że powinien dać mi ten sam obiekt, jak to zwykle bywa ze wspólną mapą: Object.map ({prop: 1}, el => el), ale zwraca klucze o wartości tej samej nazwy klucza ...
Gleb Dolzikov
17

Przyszedłem tutaj, szukając odpowiedzi na mapowanie obiektu na tablicę i otrzymując odpowiedź. Jeśli przyszedłeś tutaj, szukając tej samej odpowiedzi, co ja, oto jak możesz zmapować i sprzeciwić się tablicy.

Możesz użyć map, aby zwrócić nową tablicę z obiektu w następujący sposób:

var newObject = Object.keys(myObject).map(function(key) {
   return myObject[key];
});
JoeTron
źródło
6
To nie działa zdalnie - po prostu zwraca tablicę wartości obiektu, a nie nowy obiekt. Nie mogę uwierzyć, że ma tyle głosów.
Alnitak,
1
Masz rację, to nie odpowiada poprawnie na pytanie, ponieważ szukali odpowiedzi na mapowanie obiektu na obiekt. Przybyłem tutaj, szukając odpowiedzi na mapowanie obiektu na tablicę, i w rezultacie otrzymałem tę stronę, więc zostawię swoją odpowiedź i edytuję ją, aby odzwierciedlić, że jest to alternatywna odpowiedź dla osób, które mogły tu dotrzeć w wyniku szuka odwzorowania obiektu na tablice.
JoeTron,
5
W ES7 będzie to trywialnieObject.values(myObject)
Alnitak,
1
To miło wiedzieć, ale niektórzy ludzie nadal muszą używać starszego kodu.
JoeTron,
const obj = {foo: 'bar', baz: 42}; console.log (Object.entries (obj)); // [['foo', 'bar'], ['baz', 42]]
Bashirpour
12

Przyjęta odpowiedź ma dwie wady:

  • Nadużywa Array.prototype.reduce , ponieważ redukcja oznacza zmianę struktury typu złożonego, co nie zdarza się w tym przypadku.
  • Nie jest szczególnie przydatny do ponownego użycia

Podejście funkcjonalne ES6 / ES2015

Należy pamiętać, że wszystkie funkcje są zdefiniowane w formie 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));

  • W linii A ozostaje przypisany ponownie. Ponieważ Javascript przekazuje wartości referencyjne poprzez udostępnienie , ogenerowana jest płytka kopia pliku . Jesteśmy teraz w stanie mutować owewnątrz omapbez mutacjio w zakresie nadrzędnym.
  • W wierszu B mapwartość zwracana jest ignorowana, ponieważ mapwykonuje mutację o. Ponieważ ten efekt uboczny utrzymuje się omapi nie jest widoczny w zakresie nadrzędnym, jest całkowicie akceptowalny.

To nie jest najszybsze rozwiązanie, ale deklaratywne i wielokrotnego użytku. Oto ta sama implementacja, co w jednym wierszu, zwięzła, ale mniej czytelna:

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

Dodatek - dlaczego obiekty domyślnie nie są iterowalne?

ES2015 określił iterator i iterowalne protokoły. Ale obiekty wciąż nie są iterowalne, a zatem nie można ich zmapować. Powodem jest mieszanie danych i poziomu programu .

Społeczność
źródło
1
Wygląda na nieco przerobiony; Liczę 6 funkcji, które są w zasadzie pętlą. Inne rozwiązania są znacznie prostsze, a zaakceptowana odpowiedź jest 5 razy szybsza.
fregante
4
@ bfred.it Jaki jest cel Twojego komentarza? Jeśli lubisz mikrooptymalizację, nie powinieneś jej używać Array.prototype.reduce. Wszystko, co chcę tutaj zilustrować, to to, że przyjęta odpowiedź w jakiś sposób „nadużywa” Array.prototype.reducei jak można zrealizować czysto funkcjonalne mapowanie. Jeśli nie jesteś zainteresowany programowaniem funkcjonalnym, po prostu zignoruj ​​moją odpowiedź.
2
„redukcja oznacza zmianę struktury typu złożonego” Nie sądzę, że to prawda. Array.reduce, który tworzy nową tablicę o dokładnie takiej samej długości lub nawet dokładnie tych samych wartościach, jest całkowicie uzasadniony. Redukcji można użyć do odtworzenia funkcjonalności mapy i / lub filtra (lub obu, jak w złożonym przetworniku, dla którego podstawą jest redukcja). W FP jest to rodzaj fold, a fold, który kończy się na tym samym typie, jest nadal fold.
Dtipson
2
@Dtipson Masz absolutną rację. A foldjest bardziej ogólne niż map(funktor). Taka była jednak moja wiedza z połowy 2016 r. To połowa wieczności: D
1
Ha, tak. Lata mi zajęło uświadomienie sobie, jak bardzo wszystkie te rzeczy są ze sobą powiązane, oraz szerszy ekosystem terminów i interfejsów. Rzeczy, które wydają się takie łatwe, okazują się niemożliwie skomplikowane, a niektóre rzeczy, które wydają się niemożliwie skomplikowane, dość starannie streszczają. :)
Dtipson,
10

JavaScript właśnie dostał nowy Object.fromEntries metodę.

Przykład

function mapObject (obj, fn) {
  return Object.fromEntries(
    Object
      .entries(obj)
      .map(fn)
  )
}

const myObject = { a: 1, b: 2, c: 3 }
const myNewObject = mapObject(myObject, ([key, value]) => ([key, value * value]))
console.log(myNewObject)

Wyjaśnienie

Powyższy kod konwertuje Obiekt na zagnieżdżoną Array ( [[<key>,<value>], ...]), którą można zamapować.Object.fromEntrieskonwertuje tablicę z powrotem na obiekt.

Fajną rzeczą w tym wzorze jest to, że możesz łatwo brać pod uwagę klucze obiektów podczas mapowania.

Dokumentacja

Obsługa przeglądarki

Object.fromEntriesjest obecnie obsługiwany tylko przez te przeglądarki / silniki , niemniej dostępne są wielopełniacze (np. @ babel / polyfill ).

HaNdTriX
źródło
2
Zasadniczo Object.fromEntriesjest przeciwieństwem Object.entries. Object.entrieskonwertuje obiekt na listę par klucz-wartość. Object.fromEntrieskonwertuje listę par klucz-wartość na obiekt.
orad
8

Wersja minimalna (es6):

Object.entries(obj).reduce((a, [k, v]) => (a[k] = v * v, a), {})
Yukulélé
źródło
Myślę, że jest to nieprawidłowe z powodu literówki, myślę, że masz na myśli => Object.entries(obj).reduce((a, [k, v]) => [a[k] = v * v, a], {}), nawiasy klamrowe zamiast nawiasów.
Alexander Mills
@AlexanderMills, mój kod jest poprawny. Przeczytaj więcej o przecinku w nawiasach: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Yukulélé 30.01.2018
rozumiesz, możesz to wyjaśnić następnym razem, ale myślę, że po to są komentarze.
Alexander Mills,
1
w zasadzie mój kod jest równoważny:Object.entries(obj).reduce((a, [k, v]) => {a[k] = v * v; return a}, {})
Yukulélé
1
Świetna odpowiedź. Object.entries().reduceto najlepsze rozwiązanie, które myślę z ES6 +. Dostalibyśmy więcej głosów pozytywnych, gdyby użył returninstrukcji w reduktorze zamiast implicit returni commaoperatora - co jest schludne, ale trudne do odczytania IMHO
Drenai
7

Dla maksymalnej wydajności.

Jeśli Twój obiekt nie zmienia się często, ale wymaga częstej iteracji, sugeruję użycie natywnej mapy jako pamięci podręcznej.

// 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 już działa w Chrome, Edge, Firefox i Beta Opera, więc jest to funkcja przyszłościowa. Jest z ES7, więc polyfill to https://github.com/es-shims/Object.entries dla IE, gdzie to nie działa.

Paweł
źródło
Co powiesz na użycie Destructing ?
Константин Ван
1
@ K._ to było bardzo wolne, ale teraz nie wiem o wydajności. Wygląda na to, że wersja
Paweł
Lub, mniej wydajny ES7, ale niezmienny:const fn = v => v * 2; const newObj = Object.entries(myObject).reduce((acc, [k,v]) => Object.assign({}, acc, {[k]: fn(v)}), {});
mikebridge
Zastanawiam się, dlaczego żadna z pozostałych odpowiedzi nie korzysta z Object.entries. Jest o wiele czystszy niż iteracja po klawiszach i używanie obj [key].
corwin.amber
6

możesz użyć mapmetody i forEachtablic, ale jeśli chcesz jej użyć Object, możesz użyć jej z akcentem jak poniżej:

Korzystanie z 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}

Korzystanie z 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}

Lub możesz użyć innych pętli, takich jak $.eachmetoda, jak na poniższym przykładzie:

$.each(ob,function (key, value) {
  ob[key] *= value;
});
console.log(ob) //it will also log as {a: 4, b: 16, c: 36}
Haritsinh Gohil
źródło
Gdzie $jest jquery?
masterxilo,
Tak, możesz używać zarówno $, jak i jQuery
Haritsinh Gohil
1
@ Ronald źle zrozumiałem, zobacz moją zaktualizowaną odpowiedź, dziękuję, że nie oddałem głosu, ale naprawdę uświadomiłem sobie mój błąd, dziękuję za ulepszenie społeczności.
Haritsinh Gohil
@bcoughlan tak, to jest dokładnie to, co napisałem w komentarzu, to kwadraty liczb, czego więc chcesz?
Haritsinh Gohil,
1
@HaritsinhGohil Zrozumiałem, że krytykował cię za rozwiązanie jQuery. To prawda, że ​​w dzisiejszych czasach istnieje pewien impuls, aby odejść od jQuery, i oczywiście istnieje Węzeł, ale chyba że OP poprosi konkretnie o czysty JS lub Węzeł, myślę, że można założyć, że ktoś pracuje w środowisku internetowym i że jQuery prawie na pewno będzie dostępny.
abalter
4

map functionNie istnieje na Object.prototypejednak można naśladować go jak tak

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;
});
Whitneyit
źródło
Kwestie wydajności: typeof callback === 'function'absolutnie zbędne. 1) mapnie ma znaczenia bez wywołania zwrotnego 2) jeśli callbacknie jest funkcją, twoja mapimplementacja po prostu działa bez znaczenia dla pętli for. Ponadto, Object.prototype.hasOwnProperty.call( obj, key )jest trochę przesadą.
ankhzet
3

Natknąłem się na to jako pierwszy element w wyszukiwarce Google, próbując nauczyć się tego robić, i pomyślałem, że podzielę się, aby inni ludzie znaleźli to niedawno rozwiązanie, które znalazłem, które używa niezmiennego pakietu npm.

Myślę, że warto się nim dzielić, ponieważ niezmienny używa sytuacji DOKŁADNEJ OP w swojej własnej dokumentacji - poniższy kod nie jest moim własnym kodem, ale został pobrany z bieżącej dokumentacji 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 } 

Nie chodzi o to, że Seq ma inne właściwości („Seq opisuje leniwą operację, umożliwiając im efektywne połączenie wszystkich metod gromadzenia wyższego rzędu (takich jak mapa i filtr) poprzez nie tworzenie pośrednich kolekcji”) oraz że niektóre inne dane niezmiennego js struktury mogą również wykonywać tę pracę dość skutecznie.

Każdy, kto użyje tej metody, będzie oczywiście musiał npm install immutablei może przeczytać dokumenty:

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

alex.h
źródło
3

EDYCJA: Kanonicznym sposobem korzystania z nowszych funkcji JavaScript jest -

const identity = x =>
  x

const omap = (f = identity, o = {}) =>
  Object.fromEntries(
    Object.entries(o).map(([ k, v ]) =>
      [ k, f(v) ]
    )
  )

Gdzie ojest jakiś obiekt i ftwoja funkcja mapowania. Lub możemy powiedzieć, biorąc pod uwagę funkcję a -> bi obiekt o wartościach typu a, produkujemy obiekt o wartościach typu b. Jako podpis pseudo-typowy -

// omap : (a -> b, { a }) -> { b }

Oryginalna odpowiedź została napisana, aby zademonstrować potężny kombinator, mapReducektóry pozwala nam myśleć o naszej transformacji w inny sposób

  1. m, mapowanie funkcja - daje szansę przekształcenia przychodzącego elementu przed…
  2. r, funkcja redukująca - ta funkcja łączy akumulator z wynikiem zmapowanego elementu

Intuicyjnie mapReducetworzy nowy reduktor, do którego możemy podłączyć bezpośrednio Array.prototype.reduce. Ale co ważniejsze, możemy implementować naszą implementację funktora obiektów w omapprosty sposób, wykorzystując obiekt monoid Object.assigni {}.

const identity = x =>
  x
  
const mapReduce = (m, r) =>
  (a, x) => r (a, m (x))

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

Zauważ, że jedyną częścią programu, którą naprawdę musieliśmy napisać, jest sama implementacja mapowania -

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

Który mówi, biorąc pod uwagę znany przedmiot o, a niektóre klucz k, budowy obiektu i którego właściwość obliczana kjest wynikiem dzwoniąc fna wartości Kluczem jest, o[k].

Możemy uzyskać wgląd mapReduce„s potencjał sekwencjonowania jeśli najpierw streszczenieoreduce

// oreduce : (string * a -> string * b, b, { a }) -> { b }
const oreduce = (f = identity, r = null, o = {}) =>
  Object
    .keys (o)
    .reduce
      ( mapReduce
          ( k => [ k, o[k] ]
          , f
          )
      , r
      )

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

Wszystko działa tak samo, ale teraz omapmożna je zdefiniować na wyższym poziomie. Oczywiście nowe Object.entriessprawiają, że wygląda to głupio, ale ćwiczenie jest nadal ważne dla ucznia.

Nie zobaczysz tutaj pełnego potencjału mapReduce, ale dzielę się tą odpowiedzią, ponieważ interesujące jest zobaczyć, ile miejsc można zastosować. Jeśli interesuje Cię sposób jej wyprowadzenia i inne sposoby, które mogą być przydatne, zapoznaj się z tą odpowiedzią .

Dziękuję Ci
źródło
4
Użyjmy raczej Mapi Map.entriesOK: D. Mam dość wykorzystywania zwykłych obiektów jako typów danych map.
2
Zgadzam się, że Mappowinienem być użyty tam, gdzie to możliwe, ale nie zastępuje to całkowicie potrzeby korzystania z tych procedur na zwykłych obiektach JS: D
Dziękuję
2

Na podstawie odpowiedzi @Amberlamps, oto funkcja narzędzia (jako komentarz wyglądał brzydko)

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

a zastosowanie to:

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}
yonatanmn
źródło
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 }
Dmitry Ragozin
źródło
enumerabledomyślniefalse
Dziękuję
2

Moja odpowiedź jest w dużej mierze oparta na najwyżej ocenianej odpowiedzi tutaj i mam nadzieję, że wszyscy rozumieją (mają to samo wytłumaczenie na moim GitHubie). Oto dlaczego jego impementacja mapą działa:

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

Celem tej funkcji jest pobranie obiektu i zmodyfikowanie oryginalnej zawartości obiektu za pomocą metody dostępnej dla wszystkich obiektów (zarówno obiektów, jak i tablic) bez zwracania tablicy. Prawie wszystko w JS jest przedmiotem i dlatego elementy znajdujące się w dalszej części procesu dziedziczenia mogą potencjalnie technicznie wykorzystać te dostępne dla osób znajdujących się powyżej linii (i na odwrót).

Powodem tego jest to, że funkcje .map zwracają tablicę WYMAGAJĄCĄ podania jawnego lub niejawnego RETURN tablicy zamiast po prostu modyfikowania istniejącego obiektu. Zasadniczo oszukasz program, aby pomyślał, że obiekt jest tablicą, używając Object.keys, który pozwoli ci korzystać z funkcji mapowania z działaniem na wartości, z którymi powiązane są poszczególne klucze (faktycznie przypadkowo zwróciłem tablice, ale je naprawiłem). Dopóki nie nastąpi zwrot w normalnym sensie, nie powstanie tablica z oryginalnym obiektem w stanie nienaruszonym i zmodyfikowanym zgodnie z zaprogramowaniem.

Ten konkretny program pobiera obiekt o nazwie obrazy i pobiera wartości jego kluczy oraz dołącza znaczniki URL do użycia w innej funkcji. Oryginał to:

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

... i zmodyfikowano to:

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

Oryginalna struktura obiektu pozostaje nienaruszona, umożliwiając normalny dostęp do właściwości, o ile nie ma zwrotu. NIE każ mu zwracać tablicy jak zwykle i wszystko będzie dobrze. Celem jest PONOWNE PRZYPISANIE oryginalnych wartości (obrazów [klucz]) do tego, co jest potrzebne, a nie do niczego innego. O ile mi wiadomo, aby zapobiec wyjściu z tablicy, MUSI BYĆ PRZEKAZANIE obrazów [klucz] i nie może być domniemana ani wyraźna prośba o zwrócenie tablicy (przypisanie zmiennej robi to i było dla mnie błędne).

EDYTOWAĆ:

Zajmę się jego drugą metodą dotyczącą tworzenia nowego obiektu, aby uniknąć modyfikacji oryginalnego obiektu (a ponowne przypisanie wydaje się być nadal konieczne, aby uniknąć przypadkowego utworzenia tablicy jako wyniku). Funkcje te używają składni strzałek i są przeznaczone, jeśli chcesz po prostu utworzyć nowy obiekt do wykorzystania w przyszłości.

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

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

Sposób działania tych funkcji jest następujący:

mapFn pobiera funkcję, która zostanie dodana później (w tym przypadku (wartość) => wartość) i po prostu zwraca wszystko, co jest tam zapisane, jako wartość dla tego klucza (lub pomnożona przez dwa, jeśli zmienisz wartość zwracaną tak jak on) w mapFn ( obj) [klucz],

a następnie redefiniuje pierwotną wartość powiązaną z kluczem w wyniku [klucz] = mapFn (obj) [klucz]

i zwraca operację wykonaną na wyniku (akumulator umieszczony w nawiasach zainicjowany na końcu funkcji redukcji).

Wszystko to jest wykonywane na wybranym obiekcie i JESZCZE NIE MOŻE być niejawnym żądaniem zwróconej tablicy i działa tylko przy ponownym przypisywaniu wartości, o ile wiem. Wymaga to gimnastyki umysłowej, ale zmniejsza liczbę wymaganych linii kodu, jak widać powyżej. Dane wyjściowe są dokładnie takie same, jak widać poniżej:

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

Pamiętaj, że działało to z NIE-LICZBAMI. MOŻESZ zduplikować KAŻDY obiekt, PO PROSTU POWRACAJĄCĄ WARTOŚĆ w funkcji mapFN.

Andrew Ramshaw
źródło
2

Pierwsza funkcja odpowiada na pytanie. Tworzy mapę.

Druga funkcja nie tworzy mapy. Zamiast tego używa pętli przez właściwości w istniejącym obiekcie za pomocą hasOwnProperty(). Ta mapa alternatywna może być lepszym rozwiązaniem w niektórych sytuacjach.

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


//creates a map (answers question, but function below is 
//much 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
}


//Doesn't create a map, just applies the function to
//a specific property using existing data
function getValue(key) {
  for (var k in myObject) {
    if (myObject.hasOwnProperty(key)) {
      var value = myObject[key]
      return value * value; //stops iteration
    }
  }
}
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>

Victor Stoddard
źródło
2
To nie odpowiada na pierwotne pytanie - nie odwzorowuje wartości na nowy obiekt za pomocą podanej funkcji (tzn. Nie jest to podobne do Array.prototype.map).
Matt Browne,
@MattBrowne Dodałem drugą metodę, która tworzy prawdopodobnie mapę. Jest wolny, ale robi to, o co prosi OP. Pierwsza metoda jest idealna w wielu sytuacjach, dlatego użyłem funkcji pętli i właściwości.
Victor Stoddard
1

Jeśli interesuje Cię mapping nie tylko wartości, ale także klucze, napisałem Object.map(valueMapper, keyMapper), że zachowuje się w ten sposób:

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 }
MattiSG
źródło
3
Zakładam, że po prostu podałeś link jako odpowiedź, która jest niezadowolona.
Chris Wright,
Jeśli kiedykolwiek przydatna dla każdego, kto: npm install @mattisg/object.map.
MattiSG
1

Potrzebowałem wersji, która pozwoliła również modyfikować klucze (na podstawie odpowiedzi @Amberlamps i @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"}
}

Edycja: niewielka zmiana do przekazania w obiekcie początkowym {}. Pozwala to na [] (jeśli klucze są liczbami całkowitymi)

Symetryczny
źródło
1

W szczególności chciałem użyć tej samej funkcji, której użyłem do tablic dla pojedynczego obiektu, i chciałem, aby było to proste. To działało dla mnie:

var mapped = [item].map(myMapFunction).pop();
Jason Norwood-Young
źródło
to nie jest inaczej niżvar mapped = myMapFunction(item)
Dziękuję
Jest to o wiele prostsze niż wszystkie inne rozwiązania „nauki rakietowej”, które tu widziałem. Dzięki za udostępnienie :)
Yuri Teixeira
1

Aby ściślej odpowiedzieć na to, o co dokładnie poprosił PO, PO chce obiektu:

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

mieć metodę mapy myObject.map,

podobny do Array.prototype.map, który zostałby użyty w następujący sposób:

newObject = myObject.map (funkcja (wartość, etykieta) {
    wartość zwracana * wartość;
});
// newObject jest teraz {'a': 1, 'b': 4, 'c': 9}

Imho najlepszy (mierzone na „ bliskie temu, co jest proszony ” + „No es {5,6,7} wymagane niepotrzebnie”) odpowiedź byłaby:

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

Powyższy kod unika celowego używania jakichkolwiek funkcji językowych, dostępnych tylko w najnowszych edycjach ECMAScript. Dzięki powyższemu kodowi problem można rozwiązać, np .:

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);
alternatywny kod testu tutaj

Oprócz tego, że niektórzy czują się niezadowoleni, istnieje możliwość wstawienia rozwiązania do takiego łańcucha prototypów.

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

Coś, co przy dokładnym nadzorze nie powinno mieć żadnych złych skutków i nie powinno wpływać na mapmetodę innych obiektów (np. Macierzy map).

ludzkośćANDpeace
źródło
1

Zdefiniuj funkcję mapEntries.

mapEntries przyjmuje funkcję wywołania zwrotnego, która jest wywoływana przy każdym wpisie w obiekcie z wartościami parametrów, kluczem i obiektem. Powinien zwrócić nową wartość.

mapEntries powinien zwrócić nowy obiekt z nowymi wartościami zwróconymi z wywołania zwrotnego.

Object.defineProperty(Object.prototype, 'mapEntries', {
  enumerable: false,
  value: function (mapEntriesCallback) {
    return Object.fromEntries(
      Object.entries(this).map(
        ([key, value]) => [key, mapEntriesCallback(value, key, this)]
      )
    )
  }
})


// Usage example:

var object = {a: 1, b: 2, c: 3}
var newObject = object.mapEntries(value => value * value)
console.log(newObject)
//> {a: 1, b: 4, c: 9}

Edycja: poprzednia wersja nie określała, że ​​nie jest to właściwość wyliczalna

Arye Eidelman
źródło
0

Hej, napisałem małą funkcję mapera, która może pomóc.

    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];
                }
              }
            }
         }
      }
Zak
źródło
1
Chyba miałeś na myśli „ja” zamiast „Hej”
Carlo
0

Innym podejściem jest użycie niestandardowej funkcji strunowania json, która może również działać na głębokich obiektach. Może to być przydatne, jeśli zamierzasz opublikować go na serwerze jako 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))

Nieskończony
źródło
0

Obsługuję tylko ciągi znaków w celu ograniczenia wyjątków:

Object.keys(params).map(k => typeof params[k] == "string" ? params[k] = params[k].trim() : null);
Idan
źródło
0

Mapowanie obiektów w TypeScript

Lubię przykłady, które wykorzystują Object.fromEntriestakie jak ten , ale nadal nie są one łatwe w użyciu. Odpowiedzi, które wykorzystują, Object.keysa następnie sprawdzają, w keyrzeczywistości wykonują wiele przeglądów, które mogą nie być konieczne.

Chciałem tam była Object.mapfunkcja, ale możemy tworzyć własne i nazywają to objectMapz możliwością modyfikacji zarówno keya value:

Użycie (JavaScript):

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

// keep the key and modify the value
let obj = objectMap(myObject, val => val * 2);
// obj = { a: 2, b: 4, c: 6 }


// modify both key and value
obj = objectMap(myObject,
    val => val * 2 + '',
    key => (key + key).toUpperCase());
// obj = { AA: '2', BB: '4', CC: '6' }

Kod (TypeScript):

interface Dictionary<T> {
    [key: string]: T;
}

function objectMap<TValue, TResult>(
    obj: Dictionary<TValue>,
    valSelector: (val: TValue, obj: Dictionary<TValue>) => TResult,
    keySelector?: (key: string, obj: Dictionary<TValue>) => string,
    ctx?: Dictionary<TValue>
) {
    const ret = {} as Dictionary<TResult>;
    for (const key of Object.keys(obj)) {
        const retKey = keySelector
            ? keySelector.call(ctx || null, key, obj)
            : key;
        const retVal = valSelector.call(ctx || null, obj[key], obj);
        ret[retKey] = retVal;
    }
    return ret;
}

Jeśli nie używasz TypeScript, skopiuj powyższy kod do TypeScript Playground, aby uzyskać kod JavaScript.

Również powodem kładę keySelectorpo valSelectorliście parametrów, ponieważ jest to opcjonalne.

* Niektóre podziękowania należą się odpowiedzi Aleksandra Millsa .

orad
źródło
0
const mapObject = (targetObject, callbackFn) => {
    if (!targetObject) return targetObject;
    if (Array.isArray(targetObject)){
        return targetObject.map((v)=>mapObject(v, callbackFn))
    }
    return Object.entries(targetObject).reduce((acc,[key, value]) => {
        const res = callbackFn(key, value);
        if (!Array.isArray(res) && typeof res ==='object'){
            return {...acc, [key]: mapObject(res, callbackFn)}
        }
        if (Array.isArray(res)){
            return {...acc, [key]: res.map((v)=>mapObject(v, callbackFn))}
        }
        return {...acc, [key]: res};
    },{})
};
const mapped = mapObject(a,(key,value)=> {
    if (!Array.isArray(value) && key === 'a') return ;
    if (!Array.isArray(value) && key === 'e') return [];
    if (!Array.isArray(value) && key === 'g') return value * value;
    return value;
});
console.log(JSON.stringify(mapped)); 
// {"b":2,"c":[{"d":2,"e":[],"f":[{"g":4}]}]}

Ta funkcja przechodzi rekurencyjnie przez obiekt i tablice obiektów. Atrybuty można usunąć, jeśli zostaną zwrócone niezdefiniowane

Stanisław
źródło