#javascript #ramda.js
#javascript #ramda.js
Вопрос:
Я изо всех сил пытаюсь извлечь массив правил из отчета о тестировании JSON. Кто-нибудь может указать мне правильное направление?
// input
const json = {
"data": {
"allTests": [
{
"id": "foo",
"testStatus": "PASS",
"ruleList": [
{
"outcome": "PASS",
"name": "Image should be awesome"
}
]
},
{
"id": "bar",
"testStatus": "FAIL",
"ruleList": [
{
"outcome": "HARD_FAIL",
"name": "Image should be awesome"
}
]
},
{
"id": "baz",
"testStatus": "FAIL",
"ruleList": [
{
"outcome": "SOFT_FAIL",
"name": "Image should be awesome"
}
]
},
]
}
}
Ожидаемый результат:
[{
"name": "Image should be awesome",
"HARD_FAIL": 1,
"SOFT_FAIL": 1,
"PASS": 1
}]
Комментарии:
1. Конечным результатом должен быть массив или объект?
Ответ №1:
(Я взял на себя смелость работать только с json.data.allTests
)
Что бы я сделал:
- Извлеките все правила во всех
ruleList
сchain
- Пока вы это делаете, верните
outcome
свойство, например{outcome: 'PASS'}
, =>{PASS: 1}
- Группировать по
name
, суммируя все результаты (предполагая, что, напримерPASS: 2
, возможно) - Извлечь все значения
const with_ramda =
pipe(
chain(x => x.ruleList.map(({outcome, name}) => ({[outcome]: 1, name}))),
reduceBy(({name: _, ...acc}, x) => mergeWith(add, acc, x), {}, prop('name')),
values);
console.log(with_ramda(input));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>
<script>const {pipe, chain, reduceBy, mergeWith, add, prop, values} = R;</script>
<script>
const input =
[ { "id": "foo"
, "testStatus": "PASS"
, "ruleList":
[ { "outcome": "PASS"
, "name": "Image should be awesome"
}
]
}
, { "id": "bar"
, "testStatus": "FAIL"
, "ruleList":
[ { "outcome": "HARD_FAIL"
, "name": "Image should be awesome"
}
]
}
, { "id": "baz"
, "testStatus": "FAIL"
, "ruleList":
[ { "outcome": "SOFT_FAIL"
, "name": "Image should be awesome"
}
]
}
];
</script>
В случае, если вам интересно, также возможно ванильное решение, которое не обязательно является более сложным:
const with_vanillajs =
xs =>
Object.values(
xs.flatMap(x => x.ruleList)
.reduce((acc, {outcome, name}) =>
( acc[name] = acc[name] || {name}
, acc[name][outcome] = (acc[name][outcome] || 0) 1
, acc), {}));
console.log(with_vanillajs(input));
<script>
const input =
[ { "id": "foo"
, "testStatus": "PASS"
, "ruleList":
[ { "outcome": "PASS"
, "name": "Image should be awesome"
}
]
}
, { "id": "bar"
, "testStatus": "FAIL"
, "ruleList":
[ { "outcome": "HARD_FAIL"
, "name": "Image should be awesome"
}
]
}
, { "id": "baz"
, "testStatus": "FAIL"
, "ruleList":
[ { "outcome": "SOFT_FAIL"
, "name": "Image should be awesome"
}
]
}
];
</script>
Ответ №2:
Сгладьте ruleList
массивы с помощью R.chain, сгруппируйте их по name
, и преобразуйте в пары [name, array of rules]
, сопоставьте пары и преобразуйте каждую пару в объект. Используйте R.countBy для вычисления результатов:
const { countBy, prope, pipe, chain, prop, groupBy, toPairs, map } = R
const countByOutcome = countBy(prop('outcome'))
const fn = pipe(
chain(prop('ruleList')), // flatten the ruleList
groupBy(prop('name')), // group by the name
toPairs, // convert to [name, values] pairs
map(([name, val]) => ({ // map the pairs to objects
name,
...countByOutcome(val) // count the outcomes
})),
)
const input = [{"id":"foo","testStatus":"PASS","ruleList":[{"outcome":"PASS","name":"Image should be awesome"}]},{"id":"bar","testStatus":"FAIL","ruleList":[{"outcome":"HARD_FAIL","name":"Image should be awesome"}]},{"id":"baz","testStatus":"FAIL","ruleList":[{"outcome":"SOFT_FAIL","name":"Image should be awesome"}]}]
console.log(fn(input))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>
Комментарии:
1. Я только что увидел этот вопрос, ушел и закодировал свой ответ, отвлекся на вопрос combinators, вернулся, чтобы опубликовать его, и ваш здесь с точно такой же техникой и только немного другим кодом! Сроки!
Ответ №3:
Добавляем ответ, аналогичный ответу от Ori Drori, потому что он достаточно отличается, чтобы быть интересным.
Прежде всего, одного приведенного вами примера действительно недостаточно, чтобы продемонстрировать, что именно вы хотите сделать, но в нескольких других ответах делаются те же предположения, что и я, поэтому я предполагаю, что мы все в порядке. Но, чтобы быть явным, я предполагаю, что:
1
s в результате являются счетчиками, а не логическими маркерами, которые просто указывают, что результат включен. Мои тестовые данные проверяют это, включая второй сценарий «ПРОХОЖДЕНИЯ» для имени «Изображение должно быть потрясающим».- В данных может быть больше одного
name
. В вашем примере показан только один. Я добавил еще один в свой тестовый пример. - Выходные объекты группируются по этим именам, а не по, например, по другим полям, параллельным
allTests
.
Исходя из этих предположений, я написал это:
const transform = pipe (
path (['data', 'allTests']),
chain (prop ('ruleList')),
groupBy (prop ('name')),
map (pluck ('outcome')),
map (countBy (identity)),
toPairs,
map (([name, rest]) => ({name, ...rest})),
)
const json = {data: {allTests: [{id: "foo", testStatus: "PASS", ruleList: [{outcome: "PASS", name: "Image should be awesome"}, {outcome: "HARD_FAIL", name: "Image should be chocolate"}]}, {id: "bar", testStatus: "FAIL", ruleList: [{outcome: "HARD_FAIL", name: "Image should be awesome"}]}, {id: "baz", testStatus: "FAIL", ruleList: [{outcome: "SOFT_FAIL", name: "Image should be awesome"}, {outcome: "PASS", name: "Image should be awesome"}]}]}}
console .log (transform (json))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
<script> const {pipe, path, chain, prop, groupBy, map, pluck, countBy, identity, toPairs} = R </script>
Я склонен писать функции преобразования шаг за шагом, каждый раз пытаясь приблизить мой результат к моей конечной цели. Итак, я написал это по конвейеру. Могут быть более простые способы изменить их или объединить шаги. Ори Дрори извлек полезную функцию, и мы могли бы сделать это и здесь. Но я думаю, что он читается достаточно хорошо, и вы можете проверить, что он делает, закомментировав любой хвост функций в конвейере, чтобы увидеть промежуточные результаты.
Если вы фетишист без точек, вы можете заменить последнюю строку функции на map (apply (useWith (mergeRight, [objOf('name')])))
. Я не вижу необходимости, но это привело бы к полностью бесплатному решению.
Ответ №4:
Ну, это может сработать:
var Result = [{}];
json.data.allTests.forEach (Entry => {
var Rules = Entry.ruleList || [];
Rules.forEach (Rule => {
var { outcome, name } = Rule;
console.log ({ outcome, name });
Result [ 0 ] [ outcome ] = 1;
Result [ 0 ].name = name;
});
});
console.log ({ Result });