JavaScript/lodash data transformation - javascript

var original = {
"8": [{
"temp": {
"a": 1
},
"algo_id": 1
},
{
"temp": {
"a": 2
},
"algo_id": 101
}
],
"13": {
"temp": {
"temp1": [1, 2]
},
"algo_id": 2
}
};
const values = _.values(original);
const temp = _.map(values, (v) => {
if (_.isArray(v)) {
return _.mapValues(_.keyBy(v, 'algo_id'), a => _.pick(a, 'temp'));
}
});
console.log(temp);
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Expected:
map which has algo_id as key and temp as values.
like below and so on.
{
"1": {
"temp": {
"a": 1
}
},
"101": {
"temp": {
"a": 2
}
},
"2": {
"temp": {
"temp1": [1, 2]
}
}
}
How to add key and values which are not array in the object.?

One way to do this (not using lodash) is the following:
const transform = (original) => Object.values(original)
.flat()
.reduce((all, {algo_id, ...rest}) => ({...all, [algo_id]: rest}), {})
const original ={"13": {"algo_id": 2, "temp": {"temp1": [1, 2]}}, "8": [{"algo_id": 1, "temp": {"a": 1}}, {"algo_id": 101, "temp": {"a": 2}}]}
console.log(transform(original))
But this makes the assumption that you can use the sibling of algo_id as is. Your example seems to show further processing of it that I can't see any rule for.
If your target environments don't support flat, you can replace this:
.flat()
with this:
.reduce((a, b) => a.concat(b), [])

No need to use lodash, you can do this in plain JavaScript for this.
let original = {
"8": [{
"temp": {
"a": 1
},
"algo_id": 1
},
{
"temp": {
"a": 2
},
"algo_id": 101
}
],
"13": {
"temp": {
"temp1": [1, 2]
},
"algo_id": 2
}
};
console.log(convert(original, 'algo_id'));
function convert(data, key) {
let process = function(value, key, result) {
result[value[key]] = value;
delete result[value[key]][key]; // Remove the `algo_id` key
};
return Object.keys(data).reduce((result, k, i) => {
if (Array.isArray(data[k])) {
data[k].forEach(val => process(val, key, result));
} else {
process(data[k], key, result);
}
return result;
}, {});
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
<!--
{
"1": {
"temp": {
"a": 1
}
}
}
-->

With lodash, after getting the values, flatten the mixed array of arrays and objects, use keyBy to convert back to an object with the algo_id as key, and then map the sub object to omit the algo_id property.
const { flow, partialRight: pr, values, flatten, keyBy, mapValues, omit } = _
const fn = flow(
values, // convert to a mixed array of objects and arrays
flatten, // flatten to an array of objects
pr(keyBy, 'algo_id'), // convert to object with algo_id as key
pr(mapValues, pr(omit, 'algo_id')), // remove algo_id from the sub objects
);
const original = {"8":[{"temp":{"a":1},"algo_id":1},{"temp":{"a":2},"algo_id":101}],"13":{"temp":{"temp1":[1,2]},"algo_id":2}};
const result = fn(original);
console.log(result);
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

Related

Transform a nested object using lodash

Input:
const a = {
"8": [{
"strategy": 123,
"id": 1,
"config": {
"global_dag_conf": {
"algo_v2_conf": {
"features_to_combine": [],
"segments": [],
"force_performance": false,
"min_bid": 0,
"max_bid": 13
}
}
}
}],
"13": [{
"strategy": 456,
"id": 2,
"config": {
"global_dag_conf": {
"algo_v2_conf": {
"ivr_measured": []
}
}
}
}]
}
Output:
{
"8": [
{
"global_dag_conf": {
"algo_v2_conf": {
"features_to_combine": [],
"segments": [],
"force_performance": false,
"min_bid": 0,
"max_bid": 13
}
},
"algo_id": 1
}
],
"13": [
{
"global_dag_conf": {
"algo_v2_conf": {
"ivr_measured": []
}
},
"algo_id": 2
}
]
}
I tried below solution which works fine but need to know if is there any better way to do this using lodash and JS.
result = _.map(_.keys(addtionalAlgos), (algoType) => {
addtionalAlgos[algoType] = _.map(addtionalAlgos[algoType], v => _.assign(v.config, { algo_id: v.id }));
return addtionalAlgos;
})[0]
Here's a solution without using lodash:
Use Object.entries() to get an array of key-value pairs
Create a new object by using reduce over the array
Use map to create a new array of objects.
Destructure each object to get id and config. Spread the config variable to remove one level of nesting
const input = {"8":[{"strategy":123,"id":1,"config":{"global_dag_conf":{"algo_v2_conf":{"features_to_combine":[],"segments":[],"force_performance":false,"min_bid":0,"max_bid":13}}}}],"13":[{"strategy":456,"id":2,"config":{"global_dag_conf":{"algo_v2_conf":{"ivr_measured":[]}}}}]}
const output =
Object.entries(input)
.reduce((r, [key, value]) => {
r[key] = value.map(({ id, config }) => ({ algo_id: id, ...config }));
return r;
}, {})
console.log(output)
Use _.mapValues() to iterate the keys, and Array.map() with object destructuring and spread syntax to reformat the object:
const data = {"8":[{"strategy":123,"id":1,"config":{"global_dag_conf":{"algo_v2_conf":{"features_to_combine":[],"segments":[],"force_performance":false,"min_bid":0,"max_bid":13}}}}],"13":[{"strategy":456,"id":2,"config":{"global_dag_conf":{"algo_v2_conf":{"ivr_measured":[]}}}}]}
const result = _.mapValues(data,
arr => arr.map(({ id: algo_id, config }) =>
({ algo_id, ...config })
))
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
A pure Lodash solution using mapValues, map and assign methods
let data = {"8":[{"strategy":123,"id":1,"config":{"global_dag_conf":{"algo_v2_conf":{"features_to_combine":[],"segments":[],"force_performance":false,"min_bid":0,"max_bid":13}}}}],"13":[{"strategy":456,"id":2,"config":{"global_dag_conf":{"algo_v2_conf":{"ivr_measured":[]}}}}]};
let res = _.mapValues(data, arr => _.map(arr, obj => _.assign({
'algo_id': obj.id,
'global_dag_conf': obj.config.global_dag_conf
})));
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
An alternative without lodash.
The function reduce allows to generate an object which will be filled using the function map which transforms the original objects to the desired structure.
const a = { "8": [{ "strategy": 123, "id": 1, "config": { "global_dag_conf": { "algo_v2_conf": { "features_to_combine": [], "segments": [], "force_performance": false, "min_bid": 0, "max_bid": 13 } } } }], "13": [{ "strategy": 456, "id": 2, "config": { "global_dag_conf": { "algo_v2_conf": { "ivr_measured": [] } } } }] };
let result = Object.entries(a).reduce((a, [key, arr]) => {
return Object.assign(a, {[key]: arr.map(({id: algo_id, config}) => ({algo_id, ...config}))});
}, Object.create(null));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Javascript data transformation

Input:
{
"8": [{
"a": true,
"b": {
"xyz": 1
}
}, {
"a": false,
"b": {
"xyz": 2
}
}],
"13": [{
"b": {
"xyz": 4
}
}]
}
Output:
{
"8": [{
"b": {
"xyz": 2
}
}]
}
How can remove first element of each key and return the few keys of the same object using javascript and lodash library?
Without loadash do with Array#shift and Array#foreach
First convert obj to array using Object.keys
Then loop the value .And remove the first index of array using Array#shift
Then apply condition with array length is 0 remove the key value pair from main object
var obj = { "8": [{ "a": true, "b": { "xyz": 1 } }, { "a": false, "b": { "xyz": 2 } }], "13": [{ "b": { "xyz": 4 } }] };
Object.keys(obj).forEach(a => {
obj[a].shift()
obj[a] = obj[a];
if(obj[a].length == 0)
delete obj[a];
});
console.log(obj)
You could use reduce the entries returned by Object.entries() like this:
let obj={"8":[{"a":!0,"b":{"xyz":1}},{"a":!1,"b":{"xyz":2}}],"13":[{"b":{"xyz":4}}]}
let output = Object.entries(obj).reduce((acc, [key, value]) => {
if(value.length > 1)
acc[key] = value.slice(1)
return acc;
}, {})
console.log(output)
If you want to mutate the original object, loop through the object using for...in and use shift and delete like this:
let obj={"8":[{"a":!0,"b":{"xyz":1}},{"a":!1,"b":{"xyz":2}}],"13":[{"b":{"xyz":4}}]}
for (let key in obj) {
obj[key].shift()
if (obj[key].length === 0)
delete obj[key]
}
console.log(obj)
Use lodash's _.flow() with _.partialRight() to create a function that maps the values to the tail (all items but the 1st) of each array, and then uses _.omitBy() to remove empty keys:
const { flow, partialRight: pr, mapValues, tail, omitBy, isEmpty } = _
const fn = flow(
pr(mapValues, tail),
pr(omitBy, isEmpty)
)
const data = {"8":[{"a":true,"b":{"xyz":1}},{"a":false,"b":{"xyz":2}}],"13":[{"b":{"xyz":4}}]}
const result = fn(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
And the terser lodash/fp version:
const { flow, mapValues, tail, omitBy, isEmpty } = _
const fn = flow(
mapValues(tail),
omitBy(isEmpty)
)
const data = {"8":[{"a":true,"b":{"xyz":1}},{"a":false,"b":{"xyz":2}}],"13":[{"b":{"xyz":4}}]}
const result = fn(data)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>

count all values on object array - javascript

I need to count each value on the object array , the desired output should be like below
[{
"question": "question1",
"USA": 2
}, {
"question": "question1",
"AUS": 1
},
{
"question": "question2",
"item1": 2
},
{
"question": "question2",
"item1,item2": 1
}, {
"question": "question4",
"3": 1
}, {
"question": "question4",
"2": 1
}
]
Below is the input I need to transform in to the above output. I have no clue how to do with n no of question and also got issue when one question has 2 answers . sample input
[{"question1":"USA","question2":["item1"],"question4":2},
{"question1":"USA","question2":["item1"],"question4":3},
{"question1":"AUS","question2":["item1","item2"]}];
let arr=[{"question1":"USA","question2":["item1"],"question4":2},{"question1":"USA","question2":["item1"],"question4":3},{"question1":"AUS","question2":["item1","item2"]}];
//console.log(arr);
function solve(list){
var map = new Map();
var entry = null;
for(var item of list){
if(!map.has(item.question1))
map.set(item.question1, {question:'question1'});
entry = map.get(item.question1);
if(entry.hasOwnProperty(item.question1))
entry[item.question1] = entry[item.question1] + 1;
else
entry[item.question1] = 1;
if(!map.has(item.question2))
map.set(item.question2, {question: 'question2'});
entry = map.get(item.question2);
if(entry.hasOwnProperty(item.question2))
entry[item.question2] = entry[item.question2] + 1;
else
entry[item.question2] = 1;
}
return Array.from(map.values());
}
console.log(solve(arr))
You could take an object or what ever data structure you like which supports a key/value structure in a nested style and collect first all items and then reder the collected tree.
This approach uses objects, because the keys are strings, this is important for an array as key. This is joint with a comma which is sufficient for this use case.
var data = [{ question1: "USA", question2: ["item1"], question4: 2 }, { question1: "USA", question2: ["item1"], question4: 3 }, { question1: "AUS", question2: ["item1", "item2"] }],
hash = data.reduce((hash, o) => {
Object.entries(o).forEach(([question, value]) => {
var sub = hash[question] = hash[question] || Object.create(null);
sub[value] = sub[value] || { question, [value]: 0 };
sub[value][value]++;
});
return hash;
}, Object.create(null)),
result = Object.values(hash).reduce((r, sub) => [...r, ...Object.values(sub)], []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
First, obtain the countries by using reduce. Then use some nested forEach loops for the rest:
const input = [{"question1":"USA","question2":["item1"],"question4":2},
{"question1":"USA","question2":["item1"],"question4":3},
{"question1":"AUS","question2":["item1","item2"]}];
const countriesOutput = input.reduce((acc, curr) => {
if (!acc.some(e => e[curr.question1])) {
acc.push({ question: "question1", [curr.question1]: 1 });
} else {
acc.find(e => e[curr.question1])[curr.question1]++;
}
return acc;
}, []);
let questionsOutput = [];
input.forEach(item => {
Object.keys(item).forEach(key => {
if (key != "question1") {
if (Array.isArray(item[key])) {
questionsOutput.push({ question: key, [item[key].join(",")]: 1 });
} else {
questionsOutput.push({ question: key, [item[key]]: 1 });
}
}
});
});
const finalOutput = [...countriesOutput, ...questionsOutput];
console.log(finalOutput);
.as-console-wrapper { max-height: 100% !important; top: auto; }
Its a matter of summarizing the input using a dictionary (like Object) and track the duplicates. The "name" of the name/value pair can be uniquely identified by combining the question and answer with some delimiter.
const input = [{
"question1": "USA",
"question2": ["item1"],
"question4": 2
},
{
"question1": "USA",
"question2": ["item1"],
"question4": 3
},
{
"question1": "AUS",
"question2": ["item1", "item2"]
}
];
//Sum the input to an array which we can easily search for duplciates
var repeatCounter = {};
input.forEach(objItem => {
Object.keys(objItem).forEach(propItem => {
//Get the counter and the string
var s = `${propItem}-${objItem[propItem]}`;
var c = repeatCounter[s] || 0;
//Modify it or introduce it if absent
repeatCounter[s] = c + 1;
})
})
var output = Object.keys(repeatCounter).map(element => {
var ret = {'question': element.split('-')[0]}
ret[element.split('-')[1]] = repeatCounter[element];
return ret;
})
console.log(output);
.as-console-wrapper {
max-height: 100% !important;
}
Subtle adjustments such as fortifying the delimiter, converting multiple strings in to array items(as shown in the question) needs to be done on practical grounds.

Structuring data in JavaScript in a hierarchical fasion

I have the following table of strings coming from service:
A6-123 A5-234 A4-345 A3-456 A2-567
A6-123 A5-234 A4-678 A3-789 A2-890
A6-123 A5-456 A4-011 A3-021 A2-015
A6-234 A5-456 A4-567 A3-678 A2-789
[{
"a": "A2-567",
"an": "NAME1",
"b": "A3-456",
"bn": "NAME2",
"c": "A4-345",
"cn": "NAME3",
"d": "A5-234",
"dn": "NAME4",
"e": "A6-123",
"en": "NAME5"
},
{
"a": "A2-890",
"an": "NAME6",
"b": "A3-789",
"bn": "NAME7",
"c": "A4-678",
"cn": "NAME8",
"d": "A5-234",
"dn": "NAME4",
"e": "A6-123",
"en": "NAME5"
}]
I was thinking to structure it as follow, so i can display it on a hierchcal way
root: {"A6-123", "A6-234", A6-....}
data: [
{"k":"A6-123","n":"Name5", children:{"A5-234", "A5-456"},
{"k":"A5-234","n":"Name4", children:{"A4-345", "A4-678"},
{"k":"A2-567","n":"Name1", children:{}},
... could be others }
]
And I want to map all elements into a hierachy. The above structure is not required but thought that would be best.
The only disadvantage is when i have to lookup the next element inside data. In java i would have used a HashMap and pulled k into a key.
Looking for suggestions.
Some display option could look as follow (but i do not want to use a pacakge want to build the functions):
http://ivantage.github.io/angular-ivh-treeview/
The difference is that my data will be indented with 5 levels A6-A2.
For building a tree structure and a hash table for all nodes, you could iterate the given strings, split them and apply for every node a new objetc in the temporary object, if the node not exist.
At the end, you get an array with all nodes at the root and the nested structue.
function generateTree(array) {
var hash = {}, // reference to all nodes
tree = [], // result in tree structure
temp = { _: tree }; // collecting object
array.forEach(function (line) {
line.split(' ').reduce(function (r, k, i, kk) {
if (!r[k]) { // check if key exists
r[k] = { _: [] }; // if not create new object
hash[k] = { label: k }; // and hash
if (i + 1 !== kk.length) { // if node is not last leaf
hash[k].children = r[k]._; // generate children
}
r._.push(hash[k]); // push node to children prop
}
return r[k]; // take node with key
}, temp); // for next iteration
});
return { tree: tree, hash: hash };
}
var data = ['A6-123 A5-234 A4-345 A3-456 A2-567', 'A6-123 A5-234 A4-678 A3-789 A2-890', 'A6-123 A5-456 A4-011 A3-021 A2-015', 'A6-234 A5-456 A4-567 A3-678 A2-789'];
console.log(generateTree(data));
.as-console-wrapper { max-height: 100% !important; top: 0; }
$scope.root = [];
$scope.data = [];
$scope.loadDataToMemory = function (data) {
angular.forEach(data, function (value, key) {
if ($.inArray(value.e, $scope.root) === -1) {
$scope.root.push(value.e);
}
addToMap(value.a, value.an, "");
addToMap(value.b, value.bn, value.a);
addToMap(value.c, value.cn, value.b);
addToMap(value.d, value.dn, value.c);
addToMap(value.e, value.e, value.d);
});
}
addToMap = function (pKey, pName, pChild) {
if (!$scope.data[pKey]) {
cSet = [];
$scope.data[pKey] = { name: pName, children: cSet };
} else {
if ($.inArray(pChild, $scope.data[pKey].children) === -1) {
$scope.data[pKey].children.push(pChild);
}
}
}

Merge arrays inside an array in javascript

Going through the link Merge/flatten an array of arrays in JavaScript? is somewhat what i need. But with that link and many other links shows merging of arrays of two arrays. What I have is as follows
[
{
"setter":[
{
"keyname":"Sample Size",
"cond":"=",
"value":1
}
]
},
{
"setter":[
{
"joinedcond":"and"
},
{
"keyname":"Sample Size",
"cond":"=",
"value":2
}
]
}
]
That is I have an array and inside that array I have an array "setter".
What I want is actually merging all setter array as a single array. Having said the merge should produce below output
[
{
"setter":[
{
"keyname":"Sample Size",
"cond":"=",
"value":1
},
{
"joinedcond":"and"
},
{
"keyname":"Sample Size",
"cond":"=",
"value":2
}
]
}
]
Help would be appreciated. Thanks
You can do it by using Array#reduce
var arr = [{"setter":[{"keyname":"Sample Size","cond":"=","value":1}]},{"setter":[{"joinedcond":"and"},{"keyname":"Sample Size","cond":"=","value":2}]}];
var finalArr = arr.reduce((a,x)=>{
(a[0].setter = a[0].setter || []).push(...x.setter);
return a;
},[{}]);
console.log(finalArr);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use a hash table for the outer keys and concat the inner values with a dynamic approach for the outer keys, like setter.
var data = [{ setter: [{ keyname: "Sample Size", cond: "=", value: 1 }] }, { setter: [{ joinedcond: "and" }, { keyname: "Sample Size", cond: "=", value: 2 }] }],
result = data.reduce(function (hash) {
return function (r, o) {
Object.keys(o).forEach(function (k) {
if (!hash[k]) {
hash[k] = {};
r.push(hash[k]);
}
hash[k][k] = (hash[k][k] || []).concat(o[k]);
});
return r;
};
}(Object.create(null)), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Using ES6 Rest and Spread operators we can achieve the same with a recursive function call:
let cdata= data.slice();//Copy the data
let condensedArray=[];
function flatten(data, ...rest){
let [{ setter }] = data;
condensedArray = [...condensedArray, ...setter];
data.splice(0,1);
data.length>0?flatten(data, ...data):console.log('Complete');
}
flatten(cdata, ...cdata);
console.log(condensedArray);

Categories

Resources