Обновить значение объекта Ramda - PullRequest
1 голос
/ 04 ноября 2019

В предыдущем вопросе я попытался сгруппировать массивы по родительским идентификаторам, а затем удалить их из каждого объекта - Группировать массивы по родительскому идентификатору объекта Ramda .

Но теперь у меня возникла новая проблема. Например, я хочу обновить заголовок в объекте с идентификатором 12.

Моя модель данных:

const stuff = {
  "31": [
    {
      "id": "11",
      "title": "ramda heeeelp"
    },
    {
      "id": "12",
      "title": "ramda 123"
    }
  ],
  "33": [
    {
      "id": "3",
      "title": "..."
    }
  ],
  "4321": [
    {
      "id": "1",
      "title": "hello world"
    }
  ]
}

Попытки:

const alter = (id, key, value) => pipe(
  values,
  flatten,
  update(...) // <= end of my attempts
  // then group again
)

alter('12', 'title', 'new heading 123')(stuff)

Ответы [ 3 ]

2 голосов
/ 04 ноября 2019

Я думаю, что лучше всего это сделать с помощью пользовательского lens. Здесь я пишу создатель объектива (idInObjLens), который фокусируется на объекте с правильным идентификатором, независимо от того, в какую группу он попадает. Затем я пишу myLens, чтобы принять идентификатор и имя свойства и вернуть вам объектив, который фокусируетсяна это свойство объекта.

С ними мы можем использовать Рамда view, set и overчтобы увидеть значение, установите его или обновите значение с помощью функции:

const idInObjLens = (id) => lens (
  (obj) => {
    let index = -1
    const child = find (value => (index = findIndex (propEq ('id', id), value)) > -1, values (obj) )
    if (child) return child [index]
  },
  (val, obj) => {
    let index = -1
    const [key, value] = find (([key, value]) => (index = findIndex (propEq ('id', id), value)) > -1, toPairs (obj) )
    return assoc (key, update (index, val, value), obj)
  },
)


const myLens = (id, key) => compose (idInObjLens (id), lensProp (key))

const stuff = {"31": [{"id": "11", "title": "ramda heeeelp"},  {"id": "12", "title": "ramda 123"}], "33": [{"id": "3", "title": "..."}], "4321": [{"id": "1", "title": "hello world"}]};

[
    view (myLens ('12', 'title'), stuff),
    set  (myLens ('3',  'title'), 'new heading 123', stuff),
    over (myLens ('1',  'title'), toUpper, stuff),
] .forEach (x => console .log (x))
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {lens, find, findIndex, propEq, values, toPairs, assoc, update, compose, lensProp, view, set, over, toUpper } = R</script>

Обратите внимание, что set и over будут работать, только если этот идентификатор действительно существует. Сначала вы можете проверить, используя view.

myLens просто;это просто, как работает состав линз. (Обратите внимание, что кажется, что это происходит в обратном направлении от обычной композиции; технические причины интересны, но выходят за рамки SO-ответа.) Но idInObjLens более сложный. Как и во всех объективах, требуется геттер и сеттер. Оба они одновременно находят ключ объекта, который содержит элемент с идентификатором и индексом этого ключа в массиве, связанном с этим ключом объекта. Получатель просто возвращает значение. Сеттер использует assoc для обновления внешнего объекта и update для обновления массива внутри него. Все остальные вложенные объекты просто возвращаются по ссылке.

Это не тот код, которым можно гордиться. Это работает, и, конечно, это главное. Но мне действительно не нравится вычислять индекс массива как побочный эффект вызова find. Тем не менее, вычисление этого во второй раз кажется излишним. Мне также не очень нравится имя idInObjLens, и я всегда чувствую, что если у меня нет хорошего имени, я упускаю что-то фундаментальное. (У меня нет того же возражения против myLens, так как я предполагаю, что у вас будет более подходящее название для этого варианта использования.)

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

1 голос
/ 04 ноября 2019

Вы отображаете весь массив внутри свойств и используете R.when, чтобы развить все объекты с соответствующими id s, и замените значение свойства (title в вашем случае):

const { curry, map, when, propEq, evolve, always } = R

const fn = curry((id, prop, content) =>
  map(map( // map all objects of all properties
    when(
      propEq('id', id),  // if the id of an object matches
      evolve({ [prop]: always(content) })) // evolve it's property to the content
    )
  ))

const data = {"31":[{"id":"11","title":"ramda heeeelp"},{"id":"12","title":"ramda 123"}],"33":[{"id":"3","title":"..."}],"4321":[{"id":"1","title":"hello world"}]}

const result = fn('12', 'title', 'new heading 123')(data)

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js" integrity="sha256-xB25ljGZ7K2VXnq087unEnoVhvTosWWtqXB4tAtZmHU=" crossorigin="anonymous"></script>
1 голос
/ 04 ноября 2019

Вы можете использовать линзы здесь:

  1. Более: https://ramdajs.com/docs/#over
  2. Набор: https://ramdajs.com/docs/#set

const titleLens = R.curry((key, id, data) => R.lensPath([
  key, 
  R.findIndex(R.whereEq({ id }), R.propOr([], key, data)), 
  'title'
]));

// ----
const stuff = {
  "31": [
    {
      "id": "11",
      "title": "ramda heeeelp"
    },
    {
      "id": "12",
      "title": "ramda 123"
    }
  ],
  "33": [
    {
      "id": "3",
      "title": "..."
    }
  ],
  "4321": [
    {
      "id": "1",
      "title": "hello world"
    }
  ]
}

const title3111 = titleLens('31', '11', stuff);
const result = R.set(title3111, 'DID RAMDA HELP YOU?', stuff);

console.log('result', result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js" integrity="sha256-xB25ljGZ7K2VXnq087unEnoVhvTosWWtqXB4tAtZmHU=" crossorigin="anonymous"></script>
...