Restructure Javascript Object by grouping and Cartesian product - javascript

I have some raw javascript returned from an api that looks like this:
{"Values":
[
{
"fieldValue": 1,
"fieldName": "A"
},
{
"fieldValue": 2,
"fieldName": "A"
},
{
"fieldValue": "FOO",
"fieldName": "B"
},
{
"fieldValue": "BAR",
"fieldName": "B"
}
]
}
I want to restructure it in a way that requires grouping of attributes, converting attributes to values and a Cartesian join that results in an array of objects that looks like this:
[{"A":1,"B":"FOO"},{"A":2,B:"FOO"},{"A":1,"B":"BAR"},{"A":2,"B":"BAR"}]
I've been looking at the loDash and loDash.product library that is helpful but doesn't quite fer me there. The _groupby gives me an object of arrays rather than an array of objects:
{object:
[fieldName:"A",fieldValue:1],[fieldName:"A",fieldValue:2],[fieldName:"B",fieldValue:1],[fieldName:"B",fieldValue:2]
}

First, create an object using the given data and collect the keys and their values.
{
A: [1, 2],
C: ["FOO", "BAR"]
}
Then, get the Cartesian product of this object.
The function getCartesian separates all key/value pairs and builds a new Cartesian product by iterating over the values, if an array with objects call getCartesian again, and builds new objects.
This works for nested objects as well.
The algorithm is pretty simple, because it takes any property with a value, not just an array or object, and keeps this value and iterates over all other properties, which are arrays or objects. This algorithm keeps the inner structure and takes only the primitive values as result value for the given structure.
At the beginning, it takes an object/array, gets all entries and iterates them using an array with an empty object.
An empty array temp is the new result.
For creating new elements, the accumulator r is iterated and new values are collected. This is the part where the first level of a Cartesian product is made.
For a deeper level, a value is checked as well as "if an object", then a recursive call is made and the new result is taken for the actual key.
function getCartesian(object) {
return Object.entries(object).reduce((r, [k, v]) => {
var temp = [];
r.forEach(s =>
(Array.isArray(v) ? v : [v]).forEach(w =>
(w && typeof w === 'object' ? getCartesian(w) : [w]).forEach(x =>
temp.push(Object.assign({}, s, { [k]: x }))
)
)
);
return temp;
}, [{}]);
}
var data = { Values: [{ fieldValue: 1, fieldName: "A" }, { fieldValue: 2, fieldName: "A" }, { fieldValue: "FOO", fieldName: "C" }, { fieldValue: "BAR", fieldName: "C" }] },
temp = data.Values.reduce((r, { fieldName, fieldValue }) => {
(r[fieldName] = r[fieldName] || []).push(fieldValue);
return r;
}, {}),
cartesian = getCartesian(temp);
console.log(cartesian);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Add property to common items in array and array of objects

I have an array like ['A','B','F'] and an array of objects
[
{
name:'A',
prop1:'value1'
},
{
name:'C',
prop3:'value3'
},
{
name:'E',
prop7:'value7'
},
]
I want to filter out objects where name exists in array and add property exists:true to the array of objects and return the updated array of objects.
So for the above case it should return
[
{
name:'A',
prop1:'value1',
exists:true
},
{
name:'C',
prop3:'value3'
},
{
name:'E',
prop7:'value7'
},
]
I suppose it could be done with help of JavaScript Sets but not sure how. Also can this be done in linear complexity?
To achieve linear time complexity, you can turn your character array into a Set. Now just simply loop through the array of objects.
const characters = ['A','B','F'];
const objects = [
{
name:'A',
prop1:'value1'
},
{
name:'C',
prop3:'value3'
},
{
name:'E',
prop7:'value7'
},
];
const charSet = new Set(characters);
const output = objects.map(obj => {
if (charSet.has(obj.name)) obj.exists = true;
return obj;
});
This takes O(n + m), where n is the length of the character array, and m is the length of the object array.

How to convert JSON to a key-value dictionary using typescript?

I have the following json example:
{
"MyTest:": [{
"main": {
"name": "Hello"
},
"test2": {
"test3": {
"test4": "World"
},
"test5": 5
}
},
{
"main": {
"name": "Hola"
},
"test6": [{
"name": "one"
},
{
"name": "two"
}
]
}
]
}
I'm trying to convert it to an array of arrays with key-values
[[main.name: "Hello",test2.test3.test4: "World", test2.test5: 5] ,
[main.name = "Hola", test6.name: "one", test6.name: "two"] ];
Looking for some function like "is leaf" - so I will know that is the value.
Any advise for deep iteration will be very appriciated.
The flattenObject() function returns an object of one level, with the keys built from all sub keys. The recursive function checks if the current value is an object. If it is, it iterates the object with _.flatMap() and calls itself on each property with the keys collected so far. If the value is not an object, it returns an object with a single property (the joined keys), and the value.
It then merges the array of { key: value } objects to a single object.
const flattenObject = val => {
const inner = (val, keys = []) =>
_.isObject(val) ? // if it's an object or array
_.flatMap(val, (v, k) => inner(v, [...keys, k])) // iterate it and call fn with the value and the collected keys
:
{ [keys.join('.')]: val } // return the joined keys with the value
return _.merge({}, ...inner(val))
}
const obj = {"MyTest":[{"main":{"name":"Hello"},"test2":{"test3":{"test4":"World"},"test5":5}},{"main":{"name":"Hola"},"test6":[{"name":"one"},{"name":"two"}]}]}
const result = obj.MyTest.map(flattenObject)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

Finding an array object ID by property

This may be a duplicate, but I'm not sure.
I have the following array:
[
{
id: "object1"
},
{
id: "object2"
},
{
id: "object3"
}
]
The trick is, the array is dynamic, and, therefore, the global IDs of this array objects vary. For example, array[1] in one case may be an object with id "object1", and, in other case, that with an id of "object3".
How to query this array based on the id string and have the array index as the output?
reduce into an object indexed by id, with the values being the index of that id's object in the original array, and then you can use simple object lookup:
const input = [
{
id: "object1"
},
{
id: "object2"
},
{
id: "object3"
}
];
const indexedById = input.reduce((a, { id }, i) => {
a[id] = i;
return a;
}, {});
console.log(indexedById.object2); // index of 1 in input
.findIndex is another possibility, but it has worse time complexity than object lookup.
Array has a findIndex, so you could do const findById = (x) => xs.findIndex(({ id }) => id === x) where x is your id string and xs is your array of objects.

Sorting date values inside an array that also contains objects

I have the following data structure:
var array = [
2016-11-24: Object,
2016-11-25: Object,
2016-11-26: Object,
2016-11-27: Object,
2016-11-28: Object
]
I want to sort the array from oldest data to newest but I'm having some trouble getting it to work. I been following this resource (Sort Javascript Object Array By Date) but the array won't sort.
I believe the object may be affecting this but I'm not sure of how to use sort() when arrays have objects inside of them.
Anything that I'm missing?
Edit:
More information. I'm building this through an .each() function that looks like this:
var retentionValues = [];
jQuery.each(results.values(), function( a, b ) {
var retentionData = {};
//Go deeper into the object under each data
$.each(b, function(c, d){
//Add People values into object
if (c === "first") {
retentionData["People"] = d;
} else { //Add values for specific days or weeks
//Go into object under counts
$.each(d, function(e, f){
retentionData["Retention Day " + e] = f;
})
}
})
//Push dates into array and create object for data
retentionValues[a] = retentionData;
});
I need the key of the array to be the date since I'm passing this to another function but I need to sort the data before then.
It looks like your array is not valid, as Nina Scholz said.
This is one of the ways how you can organize your data and sort it:
var array = [
{ date:'2016-11-24', obj: Object},
{ date:'2016-11-25', obj: Object},
{ date:'2016-11-22', obj: Object},
{ date:'2016-11-27', obj: Object},
{ date:'2016-11-28', obj: Object}
];
var sortedArr = array.sort(function (a, b) {
return (new Date(a.date) > new Date(b.date))
});
Assuming a valid array with objects with a date property, you could treat an ISO 8601 date string as string, without converting to a date object, because it is directly sortable.
Array#some sorts in-situ, that means the original array is sorted.
var array = [{ date: '2016-11-24', obj: { pos: 0 } }, { date: '2016-11-25', obj: { pos: 1 } }, { date: '2016-11-22', obj: { pos: 2 } }, { date: '2016-11-27', obj: { pos: 3 } }, { date: '2016-11-28', obj: { pos: 4 } }];
array.sort(function (a, b) {
return a.date.localeCompare(b.date);
});
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Partial Answer
Note that in JavaScript, arrays are made with the following syntax:
var array = [
item1,
item2,
...
];
To set up the array the way you want it, you can either make a 2D Array(see here for more details) like so:
var array = [
[2016-11-24, Object],
[2016-11-25, Object],
[2016-11-26, Object],
[2016-11-27, Object],
[2016-11-28, Object]
]
Or, alternatively, you can make an array with Objects as items, like this:
var array = [
{2016-11-24 : Object},
{2016-11-25 : Object},
{2016-11-26 : Object},
{2016-11-27 : Object},
{2016-11-28 : Object}
]

Create array of keys by sorting values

I have an object that looks like this
myList: {
id1:{
ts:'2010-01-12T00:51:00',
name:"roger"
},
id2:{
ts:'2011-01-12T05:22:00',
name: "Tom"
},
id3:{
ts:'2013-01-12T11:32:00',
name:"Jack"
}
}
I know objects cant be sorted so i wanted to know how i can generate an array of just the keys,which are sorted according to the key "ts". I want this in descending order.
So the array for the above object will be [id3,id2,id1]
once i have this array i can make operations like this where arr is sorted array and myList is the object
for(var i=0:i<arr.length;i++)
{
alert(myList[arr[i]].name);
}
var keys = Object.keys(myList).sort(function(a, b) {
if (myList[a].ts == myList[b].ts) {
return 0;
}
return myList[a].ts < myList[b].ts ? 1 : -1;
});
console.log(keys);
jsfiddle: http://jsfiddle.net/pWq2L/
Explanation:
First you export keys from the object: Object.keys(myList)
You sort using custom comparison function and in it you refer to the .ts attribute of the compared values. a and b are the keys of the compared elements
References:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
Given this data structure, and assuming there is a good reason for it to not be an array:
var myList = {
id1: {
ts: new Date('2010-01-12T00:51:00'),
name: 'Roger'
},
id2: {
ts: new Date('2011-01-12T05:22:00'),
name: 'Tom'
},
id3:{
ts: new Date('2013-01-12T11:32:00'),
name: 'Jack'
}
}
We can create and sort an array like so:
var arr = [];
Object.keys(myList).forEach(function(item) {
arr.push(myList[item]);
});
arr.sort(function(a, b) {
return a.ts > b.ts ? -1 : a.ts < b.ts ? 1 : 0;
})
One way to extend zerkms solution is to work with an object augmented with smart properties. The versatile reduce comes in handy:
var result = keys.reduce(function(arr, k, i) {
var item = {
key: k,
index: i,
value: myList[k]
};
arr.push(item);
return arr;
}, []);
//result:
[
{ index:0 , key: id3, value: {...} },
{ index:1 , key: id2, value: {...} },
{ index:2 , key: id1, value: {...} }
]
reduce is part of the ECMAScript 5th edition; so you may fill in some gap with a polyfill.
http://jsfiddle.net/pWq2L/3/

Categories

Resources