Converting an array of objects into readable strings? - javascript

I'm trying to convert an array of objects into a regular array that I can compare values against.
$scope.refRFRInfo
Array(6) [Object, Object, Object, …]
{Object {rfrInformationID: 3000, fiscalYear: 2020, rfrNumber: NaN, …}
So I have this:
let newArray = $scope.Info.map(a => Object.keys(a).map(k => a[k]))
And the results come out as:
Array(6) [Array(18), Array(18), Array(18), …]
Array(18) [3000, 2020, NaN, …]
I want the values to be labeled as their type, so "rfrInformationID" "fiscalYear" and "rfrNumber." But I don't know how to do this.
Essentially, I want to compare a single "fiscalYear" input to a list of all the $scope.Info.fiscalYear values. But it won't let me select the values for comparison when they are an object. I also need the object to remain an object so I can sort on table headers with the object intact. If there is an easier way to achieve the end result, please let me know.
EDIT:
Based on Object.entries, I came up with:
var map = new Map(Object.entries(results.data));
map.forEach(([key, value]) => $scope.rfrInfo.push(`${key}: ${value}`));
But the second line breaks. Looks to be nesting my object into another object, too.
EDIT: Here is the full function I'm working with
function init() {
ContractsService.getRFRInformation()
.then(function (results) {
$scope.Info = results.data;
angular.forEach($scope.Info, function (value) {
$scope.number.push(value.rfrNumber);
}
});
$scope.loading = false;
});
}
In the "forEach" above I passed the value of the rfrNumber only. There are 14 fields in my $scope.Info object.
Here's what I've tried:
$scope.newInfo = [];
//Closest to intended result, but doesn't set names like "fiscalYear" or "rfrNumber" so
//I can't use it for validation
$scope.Info.forEach(_ => _.rfrNumber = (+_.Number));
let newArray = $scope.Info.map(a => Object.keys(a).map(k => a[k]));
newArray;
//Produces result where everything is further nested
var map = new Map(Object.entries(results.data));
map;
//None of these are working
map.forEach(([key, value]) => $scope.newInfo.push('${key}: ${value}'));
for (var { Object: { rfrInformationID: ID, fiscalYear: FY, rfrNumber: number } } of results.data) {
$scope.newInfo.push(Object);
}
JSON.stringify(map);
map;
JSON.stringify(results.data);
results.data;
let {
results: [
{
rfrInformationID: ID,
fiscalYear: FY,
rfrNumber: number
},
],
} = metadata;
}
var arr = [];
for (var i in results.data) {
if (obj.hasOwnProperty(i)) {
arr.push([i, results.data[i]]);
}

Related

Flatten array into single dynamic object using Javascript

I want to extract object keys into a single array and values in a different array so that I can paste the headers and values into a google sheet.
I want to achieve a dynamic code so that if more fields are pulled in from the API, it can map headers with values.
//API Response Sample.
var data = [
{
"actions": [
{
"action_type": "comment",
"value": "3"
},
{
"action_type": "like",
"value": "33"
},
{
"action_type": "link_click",
"value": "1531"
},
{
"action_type": "mobile_app_install",
"value": "1049"
}
],
"spend": "8621.03",
"date_start": "2017-10-28",
"date_stop": "2017-11-26"
}
]
So far the below code is fixed not dynamic.
const sheet = SpreadsheetApp.getActiveSheet();
//flatten the objects
var actionObjects = data.map(returnAction)
//get the headers
var headers = Object.keys(actionObjects[0])
//create a 2D array for rows
var actionRows = actionObjects.map(a => headers.map(h => a[h]))
//write the headers
sheet.getRange(sheet.getLastRow() + 1, 1, 1, headers[0].length).setValues([headers]);
//write the rows
sheet.getRange(sheet.getLastRow() + 1, 1, actionRows.length, actionRows[0].length).setValues(actionRows);
}
function returnAction(data){
let action = {}
data.actions.forEach(a => action[a.action_type] = a.value)
action ['spend'] = data.spend
action ['date_start'] = data.date_start
action ['date_stop'] = data.date_stop
return action
}
Object keys into array:
const keys = Object.keys(obj);
Object values into array:
const values = Object.values(obj);
Or both in one go ...
const keys = [];
const values = [];
for (const [key,value] of Object.entries(obj)) {
keys.push(key);
values.push(value);
}
If the structure of your object does not change... maybe something like this?
const action = {};
data.forEach(obj => {
for (const [key,value] of Object.entries(obj)) {
if (Array.isArray(value)) {
for (const o of value) {
const a = Object.values(o);
action[a[0]] = a[1];
}
} else action[key] = value;
}
})
Try this:
function setResult() {
const sheet = SpreadsheetApp.getActiveSheet();
class getResults {
constructor(arr) {
this.headers = {};
this.results = [];
for (const obj of arr) {
const actions = {};
for (const [header,value] of Object.entries(obj)) {
if (Array.isArray(value)) {
for (const action of value) {
const values = Object.values(action);
actions[values[0]] = values[1];
this.headers[values[0]] = values[0]
}
} else {
actions[header] = value;
this.headers[header] = header;
}
}
this.results.push(actions);
}
}
get array() {
const headers = Object.keys(this.headers);
const results = [headers];
for (const action of this.results) {
results.push(headers.map(header => !!action[header] ? action[header] : ''));
}
return results;
}
}
const values = new getResults(data).array;
sheet.getRange(sheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
}
This is a whole function which takes in the 'data' array-object and split it out onto your spreadesheet.
Explanation:
This function is mainly written in Object constructor and Classes.
more about object constructor in JavaScript
more about classes in JavaScript
According to your sample data, there are Objects inside an Array which I believe that each of those Objects are one set of data.
So, the 1st step is using a for ... of loop to work with each data set separately with this line of code for (const obj of arr) {}, this is very much the samething as the var actionObjects = data.map(returnAction) line in your original code. more about for ... of in JavaScript
With each of your data object, it has 2 main structure,
ONE is Array: Object: {Key1: Value1, Key2: Value2},
which you want Value1 as header and Value2 as the actual value in the output.
TWO is simply Key: Value pairs where you need key as the header and value as the value as output.
To work with the given slice of data set, this line for (const [header,value] of Object.entries(obj)) {} uses another for...of loop together with Object.entries() function to deconstruct the given Object into an 2D Array where each of the array value is one Array containing a pair of [key,value] form the given object. more about Object.entries()
Afterwards, if (Array.isArray(value)) {} will check each value given by the for...of Object.entries() function, if it is an Array, it met the condition ONE, else it is condition TWO.
For condition ONE, you want to use the 1st value of the object as header, and the 2nd value as actual value.
for (const action of value) {} iterate the 'values' of the object as an array, and store the values as {value[0]: value[1]} in the object declared before entering the loop fucntion for later use.
For condition TWO, just store it into the same object as condition ONE uses in the format of {key: value}.
At the end of each loop, before going onto the next data object, push the key: value pairs stored in this loop (named as actions) into result array.
Till this step, you alread have an array which looks like this:
Array: [
Object1: {
header1: value1,
header2: value2,
header3: value3,
header4: value4,
...
},
...
]
The Object this.header {} is declarated to keep track of the length of max header column counts, and get rip of any duplicates (since Object keys cannot be duplicated). This help keep the function working even if some of your data objects may has different headers from the others.
After these loops iterate every object inside your data Array,
custom method created with getter function get array() form the final result of all data into a 2D array for the apps script .setValues() function to print it onto your spreadsheet. more about getter
If your main concern is the class and object constructor, here is another version of code without using any of them:
function setResult2() {
const sheet = SpreadsheetApp.getActiveSheet();
const headers = {};
const results = [];
for (const obj of data) {
const actions = {};
for (const [header,value] of Object.entries(obj)) {
if (Array.isArray(value)) {
for (const action of value) {
const values = Object.values(action);
actions[values[0]] = values[1];
headers[values[0]] = values[0]
}
} else {
actions[header] = value;
headers[header] = header;
}
}
results.push(actions);
}
const getArray = (results,headers) => {
const headers_final = Object.keys(headers);
const results_final = [headers_final];
for (const action of results) {
results_final.push(headers_final.map(header => !!action[header] ? action[header] : ''));
}
return results_final;
}
const values = getArray(results,headers);
console.log(values)
sheet.getRange(sheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
}

How to convert json object keys into different arrays removing the duplicate

I'm having the JSON like this i need to group this JSON with all the keys in JSON object and value should in array (excluding duplicates).
var people = [
{sex:"Male", name:"Jeff"},
{sex:"Female", name:"Megan"},
{sex:"Male", name:"Taylor"},
{sex:"Female", name:"Madison"}
];
My output should be like
{"sex":["Male","Female"],"name":["Jeff","Megan","Taylor","Madison"]}
how we can able to achieve this
function getValues(array) {
var result = {};
array.forEach(obj => {
Object.keys(obj).forEach(key => {
if(!Array.isArray(result[key]))
result[key] = [];
result[key].push(obj[key]);
})
})
return result;
}
You could use the Array.reduce() method to transform your array into a single object:
var people = [
{sex:"Male", name:"Jeff"},
{sex:"Female", name:"Megan"},
{sex:"Male", name:"Taylor"},
{sex:"Female", name:"Madison"}
];
const transformed = people.reduce((acc, e) => {
Object.keys(e).forEach((k) => {
if (!acc[k]) acc[k] = [];
if (!acc[k].includes(e[k])) acc[k].push(e[k]);
});
return acc;
}, {});
console.log(transformed);
If for one of the object keys (sex or name in this case) a value array does not exist, it is created. Before a value is pushed into any of the value arrays, it is verified that it is not already present in that array.

Sum values of collection by type and assign to new property on replicated object

I'm trying to sum values from a collection and add them to a new property for each object
var collection = [
{prop:'title',quan:2},
{prop:'body',quan:3},
{prop:'title',quan:2},
{prop:'title',quan:4},
]
/* desired result
[
{prop:'title', quan:2, stock:8},
{prop:'body', quan:3, stock:3},
{prop:'title', quan:2, stock:8},
{prop:'title', quan:4, stock:8},
]
*/
I've tried many different ways with no success. I am trying to do this in a functional way.
This were I am currently stuck and I'm quite sure it is the most concise way.
// group the props using key
var result = _.groupBy(collection,'prop');
which outputs
{
title:[
{prop:'title',quan:2},{prop:'title',quan:2},{prop:'title',quan:4}
],
body:[
{prop:'body':quan:3}
]
}
So let's reduce the arrays we've created
var obj = {};
_.forEach(result,function(value,key){
obj[key] = _.reduce(value,function(acc,val){
return acc.quan + val.quan
});
});
This section above isn't working though?
When I have that working, I should be able to map it back to my final collection.
we know totals, map them to collection
var final = _.map(collection,function(value){
return {
type:value.prop,
val:value.quan,
stock:obj[value.prop]
}
});
jsbin
First you have to get the sum object and then assign the sum to the corresponding object. Like this:
function stock(arr) {
// using lodach: var sum = _.reduce(arr, ...)
var sum = arr.reduce(function(s, o) { // get the sum object (hashing)
s[o.prop] = (s[o.prop] || 0) + o.quan; // if the sum already contains an entry for this object porp then add it to its quan otherwise add 0
return s;
}, {});
// using lodash: _.forEach(arr, ...);
arr.forEach(function(o) { // assign the sum to the objects
o.stock = sum[o.prop]; // the stock for this object is the hash of it's prop from the sum object
});
}
var collection = [
{prop:'title',quan:2},
{prop:'body',quan:3},
{prop:'title',quan:2},
{prop:'title',quan:4},
]
stock(collection);
console.log(collection);
If you want to return a new array and leave the original intact, use map like you already did instead of forEach like this:
// using lodash: _.map(arr, ...);
return arr.map(function(o) { // create new objects with stock property
return {
prop: o.prop,
quan: o.quan,
stock: sum[o.prop]
};
// or in ecmascript: return Object.assign({}, o, {stock: sum[o.prop]});
});
And then you'll have to use it like this:
var newArray = stock(collection);
I like this solution better because it abstracts away the collation but allows you to control how items are collated using a higher-order function.
Notice how we don't talk about the kind or structure of data at all in the collateBy function – this keeps our function generic and allows for it to work on data of any shape.
collateBy takes a grouping function f and a reducing function g and a homogenous array of any type of data
// collateBy :: (a -> b) -> ((c,a) -> c) -> [a] -> Map(b:c)
const collateBy = f => g => xs => {
return xs.reduce((m,x) => {
let v = f(x)
return m.set(v, g(m.get(v), x))
}, new Map())
}
const collateByProp = collateBy (x => x.prop)
const assignTotalStock = xs => {
let stocks = collateByProp ((acc=0, {quan}) => acc + quan) (xs)
return xs.map(({prop, quan}) =>
({prop, quan, stock: stocks.get(prop)}))
}
var collection = [
{prop:'title',quan:2},
{prop:'body',quan:3},
{prop:'title',quan:2},
{prop:'title',quan:4},
]
console.log(assignTotalStock(collection))
// [ { prop: 'title', quan: 2, stock: 8 },
// { prop: 'body', quan: 3, stock: 3 },
// { prop: 'title', quan: 2, stock: 8 },
// { prop: 'title', quan: 4, stock: 8 } ]
Performing collations is very common when manipulating data, so it doesn't make sense to encode collation behaviour in each function that needs it. Instead, use a generic, higher-order function that captures only the essence of a collation computation, and specialize it using grouping function f and reducing function g

How to get a number value within an object that is in an object

My code:
rbx.getPlayers(539310, 1).promise.then(players => {
console.log(players)
for (var list in players.players) {
console.log(list)
var key = Object.Key(list)
console.log(Key)
}
})
What it outputs:
{ total: 9,
players:
{ AgentJay400: 65910635,
MatthewHAndreas: 49787909,
coolguysocoolroblox: 165524669,
CAMPER5155: 45422370,
Mavnkei: 69082588,
kaankerem123: 92305180,
egehan432: 120777218,
panpanaber54: 31962303,
IXTactical_CactusXI: 17451343 } }
AgentJay400
MatthewHAndreas
coolguysocoolroblox
CAMPER5155
Mavnkei
kaankerem123
egehan432
panpanaber54
IXTactical_CactusXI
Problem:
I need the number values of each user (So {AgentJay4000: 65910635} I would want the 65910635) Node.js does not seem to have Object.keys so... I have no clue how to get the number...
Node should definitely have Object.keys. If your version doesn't you should update node. When you call Object.keys you get an array in return, so you can do awesome array things like map, reduce, forEach:
Object.keys(players.players).forEach(function(key) {
console.log(key, players.players[key])
})
If you just want the number values, then map it:
Object.keys(players.players).map(function(key) {
return players.players[key]
})
Now you have an array of the numbers only.
Try like this.You can access your object value using . operator.
Suppose you have an object:
var obj={
key1:value1,
key2:value2
}
Then access values like obj.key1 or obj['key1'].
to get all the values.Use Object.values(obj);
var obj = { total: 9,
players:
{ AgentJay400: 65910635,
MatthewHAndreas: 49787909,
coolguysocoolroblox: 165524669,
CAMPER5155: 45422370,
Mavnkei: 69082588,
kaankerem123: 92305180,
egehan432: 120777218,
panpanaber54: 31962303,
IXTactical_CactusXI: 17451343 } };
var players = obj.players;
var number_values = Object.values(players);
console.log(number_values );
You can output the keys and their associated numbers by doing the following:
rbx.getPlayers(539310, 1).promise.then(players => {
console.log(players)
for (var key in players.players) {
console.log(key, ':', players.players[key])
}
})
To demonstrate how Object.keys works an alternative method of accessing the players - this does the same as the above.
rbx.getPlayers(539310, 1).promise.then(players => {
var keys = Object.keys(players.players);
for (var i = 0; i < keys.length; i++) {
let key = keys[i];
let player = players.players[key];
console.log(key, ':', players.players[key])
}
});
The mistakes you made in your attempt were you were attempting to access Object.key which was a typo for Object.keys and attempting to obtain a list of keys from a string (as a loop such as for(var key in obj) will set key to each key in obj and all object keys are strings).

What is better way to send associative array through map/reduce at MongoDB?

Here is my functions:
map:
function () {
// initialize KEY
// initialize INDEX (0..65536)
// initialize VALUE
var arr = [];
arr[INDEX] = { val: VALUE, count: 1 };
emit(KEY, { arr: arr });
}
reduce:
function (key, values) {
var result = { arr: [] };
for (var i = 0; i < values.length; i++) {
values[i].arr.forEach(function (item, i) {
if (result.arr.hasOwnProperty(i)) {
result.arr[i].val += item.val;
result.arr[i].count += item.count ;
} else {
result.arr[i] = item;
}
});
}
As you can see, I'm trying to send associative array from map to reduce. But when I try to enumerate values of array values[i].arr.forEach I get listing 0..max_index. So, every reduce I have to enumerate a lot of undefined elements.
When I try to enumerate values of array (arr) at map I get expected result (only defined elements).
Actually, I don't sure that associative array is best solution for my task. But I can't find faster way to find element by id.
Could you please answer the questions:
Why differences of array processing at map and
reduce?
What data structure I should use (or how I should use my array) to optimize my current solution?
I decided to use object:
var arr = {};
arr[INDEX] = { val: VALUE, count: 1 };
It is works with for .. in as expected.

Categories

Resources