Spłaszczanie zagnieżdżonego obiektu

0

Hej,

Użytkownik może w gui narzędzia wykilkać sobie dane, które chce wysłać przez api(rest). Dane w obiekcie, które podaje mi narzędzie są w nie sformatowane poprawnie pod użycie ich w payloadzie.

Założenia:
Kod musi obsłużyć zmienną ilość propertiesów, mogą mieć różną wartość(string, int, array).
Podstawową walidację zapewnia mi gui.
Obiekt jest używany tylko w jednym określonym miejscu, żaden inny kod nie ma do niego dostępu w momencie użycia/stworzenia.
Kod musi obsłużyć nulla w wartości, chce pomijać dane tego typu w zapytaniu ale to prosty przypadek i dodam to po testach tego co mam.

Przykładowe dane wystawione przez 'gui':

{
  name: 'test',
  attributes: [
    {
      name: 'attr name',
      value: 'example'
    },
    {
      name: 'another attr name',
      value: 'another example'
    }
  ],
  description: 'something long and complicated'
}

Poniższy kod sformatuje to do:

{
    "name": "test",
    "attr name": "example",
    "another attr name": "another example",
    "description": "something long and complicated"
}

Czytając o dostępnych metodach wystarczajace funkcjonalnosci dadzą mi metody reduce() oraz assign().

function reduceArray (resArray) {
  return resArray.reduce(function (acc, item) {
    return Object.assign(acc, { [item.name]: item.value ?? item.id })
  }, {})
}

function parseResultsObj (resObj) {
  return Object.keys(resObj).reduce(function (acc, item) {
    if (Array.isArray(resObj[item])) {
      return Object.assign(acc, reduceArray(resObj[item]))
    } else {
      return Object.assign(acc, { [item]: resObj[item] })
    }
  }, {})
}

Chce tego koda używać w dwóch projektach(wtyczki do zewnętrznych narzędzi) i chce się upewnić co poprawić/zaorać/zmienić zanim mnie ugryzie coś czego nie przewidziałem. Docelowe obiekty są większe(max 100 properties) ale nie mają więcej poziomów zagnieżdzeń, ich struktura jest w miare stała.

Dzięki!

1

Jak już jesteśmy przy reduce, to tak na szybko:

const result = Object.entries(data).reduce((arr, [key, value]) => {
  if (Array.isArray(value)) {
    for (const item of value) {
      arr[item.name] = item.value;
    }
  } else {
    arr[key] = value;
  }
  return arr;
}, {});

Pewnie da się ładniej.

1

Jeśli masz już zrobiony algorytm, który zamienia dane tak jak chcesz to reszta tak na prawdę jest kwestią preferencji, można kombinować np z reduce, ale przy większej liczbie opcji i dodatkowej walidacji może być to mniej czytelne niż zwykła pętla for in / for of

const checkValue = (value) => value !== null && value !== undefined;

const parseResultsObj = (obj) => {
  const result = {};
  
  for (const key in obj) {
    const resultValue = obj[key];
    
    if (Array.isArray(resultValue)) {
      for (const item of resultValue) {
        if (checkValue(item.value)) {
          result[item.name] = item.value;
        } else if (checkValue(item.id)) {
          result[item.name] = item.id;
        }
      }
    } else if (checkValue(resultValue)) {
      result[key] = resultValue;
    }
  }
  
  return result;
}
0

Super, bardzo dziękuje!

Przetestowane, działa jak trzeba i troszke się nauczyłem :)

function checkValue (value) {
  return value !== null && value !== undefined && value !== ''
}

function parseResultsObj (data = {}) {
  return Object.entries(data).reduce(function (arr, [key, value]) {
    if (Array.isArray(value)) {
      for (const item of value) {
        if (checkValue(item.value)) {
          arr[item.name] = item.value
        }
      }
    } else {
      if (checkValue(value)) {
        arr[key] = value
      }
    }
    return arr
  }, {})
}

0

Zakładając, że będzie tylko jedna tablica attributes:

const parseResultsObj = obj => Object.fromEntries(
   Object.entries(obj)
  .filter(entry => entry[0] != 'attributes')
  .concat(obj.attributes.map(({name, value}) => [name, value]))
);

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries

jeśli tablic może być kilka:

const parseResultsObj = obj => Object.fromEntries(
   Object.entries(obj)
  .reduce((entries, [k, v]) => entries.concat(
      Array.isArray(v)? v.map(attr => [attr.name, attr.value]) : [[k, v]]), []
  )
);

Docelowe obiekty są większe(max 100 properties) ale nie mają więcej poziomów zagnieżdzeń, ich struktura jest w miare stała.

kod, który wrzuciłem, nie obsługuje poziomów zagnieżdżeń, ale można by je dodać za pomocą rekurencji. Sprawdzać czy coś jest obiektem v && typeof v == 'object' (ale uwaga: tablica też jest technicznie obiektem, więc też ma typeof równe 'object') i jak jest, to wjechać w rekurencję.

1 użytkowników online, w tym zalogowanych: 0, gości: 1