Reputation: 967
Suppose I have a input array like the following
var inputArray = [
{a: 1, b: 1, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 1, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 1, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 1, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 1, c: 1, d: 1, value: 5, rank: 5},
{a: 1, b: 2, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 2, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 2, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 2, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 2, c: 1, d: 1, value: 5, rank: 5}
]
I want to transform my inputArray
to the following outputArray
var outputArray = [
{
a: 1,
b: 1,
c: 1,
d: 1,
values:{
"1":{value: 1},
"2":{value: 2},
"3":{value: 3},
"4":{value: 4},
"5":{value: 5}
}
},
{
a: 1,
b: 2,
c: 1,
d: 1,
values:{
"1":{value: 1},
"2":{value: 2},
"3":{value: 3},
"4":{value: 4},
"5":{value: 5}
}
}
]
That means, I need create a dictionary for same property of a
, b
, c
and d
where the value of property rank
is the key of the dictionary and the value of the dictionary is an object
where the only property is value
.
We assume that the inputArray
will not be sorted with respect to the combination of a
, b
, c
and d
. So, my approach is like this,
(function(){
var inputArray = [
{a: 1, b: 1, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 1, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 1, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 1, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 1, c: 1, d: 1, value: 5, rank: 5},
{a: 1, b: 2, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 2, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 2, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 2, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 2, c: 1, d: 1, value: 5, rank: 5}
]
var temp = inputArray.sort(function(valA, valB){
if(valA.a === valB.a){
if(valA.b === valB.b){
if(valA.c === valB.c){
return valA.d < valB.d;
}
return valA.c < valB.c;
}
return valA.b < valB.b;
}
return valA.a < valB.a;
});
var outputArray = [],
currentIndex = 0;
for(var i = 0; i < inputArray.length; i++){
if(i > 0 && isConfigurationSame(inputArray[i], inputArray[i-1])){
outputArray[currentIndex-1].values[inputArray[i].rank] = {
value: inputArray[i].value
}
}
else{
outputArray.push(mapToOutputArrayObject(inputArray[i]));
currentIndex++;
}
}
console.log(outputArray);
function isConfigurationSame(A, B) {
return A.a === B.a
&& A.b === B.b
&& A.c === B.c
&& A.d === B.d;
}
function mapToOutputArrayObject(val){
var row = {};
row['a'] = val.a;
row['b'] = val.b;
row['c'] = val.c;
row['d'] = val.d;
row['values'] = {};
row.values[val.rank] = {
value: val.value
}
return row;
}
}());
But the problem is, this thing is really getting more time if the length of input array is huge. This multi-criteria sorting also taking much time.
Is there any better approach to accomplish the result more efficiently with less time?
Thanks for your time and patience.
UPDATE: The values of a
, b
, c
and d
can be integer or null
.
Upvotes: 3
Views: 147
Reputation: 21120
Here is another option, first grouping by a
, b
, c
and d
. Then mapping over each group transforming the value
and rank
.
var inputArray = [{a: 1, b: 1, c: 1, d: 1, value: 1, rank: 1}, {a: 1, b: 1, c: 1, d: 1, value: 2, rank: 2}, {a: 1, b: 1, c: 1, d: 1, value: 3, rank: 3}, {a: 1, b: 1, c: 1, d: 1, value: 4, rank: 4}, {a: 1, b: 1, c: 1, d: 1, value: 5, rank: 5}, {a: 1, b: 2, c: 1, d: 1, value: 1, rank: 1}, {a: 1, b: 2, c: 1, d: 1, value: 2, rank: 2}, {a: 1, b: 2, c: 1, d: 1, value: 3, rank: 3}, {a: 1, b: 2, c: 1, d: 1, value: 4, rank: 4}, {a: 1, b: 2, c: 1, d: 1, value: 5, rank: 5}];
function groupBy(array, callback) {
return array.reduce((groups, item, ...args) => {
const key = callback(item, ...args),
group = groups[key] || (groups[key] = []);
group.push(item);
return groups;
}, {});
};
console.log(
Object
.values( groupBy(inputArray, ({a, b, c, d}) => [a, b, c, d]) )
.map(group => {
const {a, b, c, d} = group[0],
values = {};
group.forEach(({value, rank}) => values[rank] = {value});
return {a, b, c, d, values};
})
);
Upvotes: 1
Reputation: 4768
Here is a stab at it using Set, Map and a const method to build the Values object.
var inputArray = [
{a: 1, b: 1, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 1, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 1, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 1, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 1, c: 1, d: 1, value: 5, rank: 5},
{a: 1, b: 2, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 2, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 2, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 2, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 2, c: 1, d: 1, value: 5, rank: 5}
];
const getValueObject = (a,b,c,d, arr) => {
let obj = {};
arr.filter(i => i.a === a &&
i.b === b &&
i.c ===c &&
i.d === d)
.forEach(item => obj[item.value] = item.rank);
return obj;
};
// Get a set based on the key a,b,c,d
let newArray = [...new Set(inputArray.map(({a,b,c,d}) => `${a},${b},${c},${d}`))]
.map(item => {
let [a,b,c,d] = item.split(',').map(i => parseInt(i));
// filter and add
return {
a: a,
b: b,
c: c,
d: d,
values: getValueObject(a,b,c,d, inputArray)
};
});
console.log(newArray);
Upvotes: 1
Reputation: 386568
You could take a Map
and an array of the grouping keys and collect the values for each group.
var array = [{ a: 1, b: 1, c: 1, d: 1, value: 1, rank: 1 }, { a: 1, b: 1, c: 1, d: 1, value: 2, rank: 2 }, { a: 1, b: 1, c: 1, d: 1, value: 3, rank: 3 }, { a: 1, b: 1, c: 1, d: 1, value: 4, rank: 4 }, { a: 1, b: 1, c: 1, d: 1, value: 5, rank: 5 }, { a: 1, b: 2, c: 1, d: 1, value: 1, rank: 1 }, { a: 1, b: 2, c: 1, d: 1, value: 2, rank: 2 }, { a: 1, b: 2, c: 1, d: 1, value: 3, rank: 3 }, { a: 1, b: 2, c: 1, d: 1, value: 4, rank: 4 }, { a: 1, b: 2, c: 1, d: 1, value: 5, rank: 5 }],
keys = ['a', 'b', 'c', 'd'],
result = [],
map = new Map;
array.forEach(o => {
var key = keys.map(k => o[k]).join('|'),
temp = map.get(key);
if (!temp) {
map.set(key, temp = Object.assign(...keys.map(k => ({ [k]: o[k] })), { values: {} }));
result.push(temp);
}
temp.values[o.rank] = { value: o.value };
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 2
Reputation: 138267
You could create a hashtable and generate a unique key based on a, b, c and d:
const hash = {};
for(const { a, b, c, d, value, rank } of array) {
const key = JSON.stringify([a, b, c, d]); // generate a unique, but not random key
if(hash[key]) { // check if it already exists,
hash[key].values[rank] = value; // merge
} else {
hash[key] = { // create a new entry
a, b, c, d,
values: { [rank]: value },
};
}
}
const result = Object.values(hash); // turn the object into an array
That is O(n), which is better as the time complexity of any .sort
implementation (but it only works if a, b, c and d are serializable (like in this case)).
Upvotes: 5