sum values in object if multiple keys are the same JS - javascript

for example i have 5 objects:
{ row: aa, col: 1, value: 1 }
{ row: bb, col: 2, value: 1 }
{ row: bb, col: 3, value: 1 }
{ row: aa, col: 1, value: 1 }
{ row: aa, col: 2, value: 1 }
i want to sum values if row and col are the same, so the output should be:
{ row: aa, col: 1, value: 2 }
{ row: bb, col: 2, value: 1 }
{ row: bb, col: 3, value: 1 }
{ row: aa, col: 2, value: 1 }
thank you for your help!
tried this:
Sum javascript object propertyA values with same object propertyB in array of objects

You can do this with reduce() and one object to store keys.
var data = [
{ row: 'aa', col: 1, value: 1 },
{ row: 'bb', col: 2, value: 1 },
{ row: 'bb', col: 3, value: 1 },
{ row: 'aa', col: 1, value: 1 },
{ row: 'aa', col: 2, value: 1 }
]
var o = {}
var result = data.reduce(function(r, e) {
var key = e.row + '|' + e.col;
if (!o[key]) {
o[key] = e;
r.push(o[key]);
} else {
o[key].value += e.value;
}
return r;
}, []);
console.log(result)

Just for completeness, with a version for variable keys, an object for grouping the parts and Array#forEach.
var data = [{ row: 'aa', col: 1, value: 1 }, { row: 'bb', col: 2, value: 1 }, { row: 'bb', col: 3, value: 1 }, { row: 'aa', col: 1, value: 1 }, { row: 'aa', col: 2, value: 1 }],
grouped = [];
data.forEach(function (a) {
var key = ['row', 'col'].map(function (k) { return a[k]; }).join('|');
if (!this[key]) {
this[key] = { row: a.row, col: a.col, value: 0 };
grouped.push(this[key]);
}
this[key].value += a.value;
}, Object.create(null));
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

What I would do is put your objects in an array then iterate over that and check on each iteration if the key of a new object matches that of an old one and load the objects into a separate array if there isn't a match. If it does match then add its value to the value of the old own. I tested the following code and it seems to work how you want.
var array = [{ row: 'aa', col: 1, value: 1 },
{ row: 'bb', col: 2, value: 1 },
{ row: 'bb', col: 3, value: 1 },
{ row: 'aa', col: 1, value: 1 },
{ row: 'aa', col: 2, value: 1 }];
var newArray = [];
for(var x in array) {
for(var y in newArray) {
var found = false;
if(array[x].row == newArray[y].row && array[x].col == newArray[y].col) {
newArray[y].value += array[x].value;
found = true;
break;
}
}
if(!found) {
newArray.push(array[x]);
}
}
console.log(newArray);

Related

Count occurences of an object field in an array and add it as an additional key

I want to create an array of object like this :
array_1 :
array(0, 0, 0, 1, 0, 1, 0, 1, 1);
object_2 :
obj: [
{
id: 0,
fonction: 'hey'
},
{
id: 1,
fonction: 'hi'
}
]
So, I want the following output :
result :
obj: [
{
id: 0, // id of object_2
max: 5, // number of id value in array_1
value: 0, // add an empty value
fonction: 'hey' // fonction text in oject_2
},
{
id: 1,
max: 4,
value: 0,
fonction: 'hi'
}
]
Thanks for your help
You can just map your existing object, and count the ids inside your array using the filter + length option
const arr = [0, 0, 0, 1, 0, 1, 0, 1, 1];
const target = [
{
id: 0,
fonction: 'hey'
},
{
id: 1,
fonction: 'hi'
}
];
console.log( target.map( item => ({
...item,
max: arr.filter( v => item.id === v ).length,
value: 0
}) ) );

Collect distances (and restore path) from 1 point to all others in a graph

Problem:
I have a weighted graph. I want to get distance from definite point to all other points in the graph.(and then get path to them)
I used modified dijkstra algorithm
So, here is code:
var get_path = function(graph, a) {
// declaration
let cache, i, v, queue, node, links, root,
j, link, c, n, d, max, w, L = graph.length;
// initialization
cache = Array(L);
i = L;
while (--i >= 0) {
v = graph[i];
cache[v.id] = {
id: v.id,
distance: Infinity,
links: v.links,
prev: null,
};
}
root = cache[a];
root.distance = 0;
queue = [root];
// processing
i = 0;
while (i < queue.length) {
node = queue[i];
links = node.links;
j = links.length;
while (--j >= 0) {
link = links[j];
c = cache[link.id];
d = node.distance + link.weight;
if (d < c.distance) {
c.prev = node;
c.distance = d;
queue.push(c);
}
}
i++;
}
return cache;
}
Graph format is:
graph = [
{
id: 1,
links: [
{
id: 2,
weight: 1,
},
{
id: 3,
weight: 1,
},
],
},
{
id: 2,
links: [
{
id: 1,
weight: 1,
},
{
id: 4,
weight: 2,
}
]
},
{
id: 3,
links: [
{
id: 1,
weight: 1,
},
{
id: 4,
weight: 3,
}
]
},
{
id: 4,
links: [
{
id: 2,
weight: 2,
},
{
id: 1,
weight: 1,
},
{
id: 3,
weight: 3,
},
{
id: 5,
weight: 1,
}
]
},
{
id: 5,
links: [
{
id: 4,
weight: 1,
}
]
}
]
Performance:
In my PC this algorithm works with graph of ~160 vertices and ~350 edges about 0.03-0.06ms. But I need faster!
You can measure performance on your PC here
Question:
How to make this code (function get_path()) faster? Is it possible on JavaScript? If I should change the format of graph to make algorithm faster it's not a problem.
Or I exhausted possibilities of JavaScript?

Replace properties of specific elements in object

here is the data:
const data = {
element6: {
col: 2,
row: 3,
text: "Col2, Row1"
},
element1: {
col: 1,
row: 1,
text: "Col1, Row1"
},
element8: {
col: 3,
row: 2,
text: "Col2, Row1"
},
element2: {
col: 1,
row: 2,
text: "Col1, Row2"
},
element5: {
col: 2,
row: 2,
text: "Col2, Row2"
},
element3: {
col: 1,
row: 3,
text: "Col1, Row3"
},
element4: {
col: 2,
row: 1,
text: "Col2, Row1"
},
element7: {
col: 3,
row: 1,
text: "Col2, Row1"
},
element9: {
col: 3,
row: 3,
text: "Col2, Row1"
},
};
What I want to do is to add a property ind (which equals to element's col value) and overwrite col (to the smallest value of selected) to all elements which are in the same row and different col.
For example, elements 2, 5 and 8 are in row: 2, so they would change to:
{
element8: {
col: 1,
row: 2,
text: "Col2, Row1",
ind: 3
},
element2: {
col: 1,
row: 2,
text: "Col1, Row2",
ind: 1
},
element5: {
col: 1,
row: 2,
text: "Col2, Row2",
ind: 2
},
}
Note, I don't want to change the order of object in data.
Thanks.
I think you can do it with a help of lodash (or any other helper library)
Assuming your data is object (by the way, the order of keys in object is not guaranteed!)
const res = _.chain(data)
.toPairs()
.groupBy(([key, val]) => val.row)
.mapValues(list => {
const smallestCol = _.minBy(list, ([key, val]) => val.col)[1].col
return list.map(([key, val]) => [key, {...val, ind: val.col, col: smallestCol}])
})
.values()
.flatten()
.fromPairs()
.value();
It may be worth noting that as of ES2019 almost all of those functions (except for groupBy and minBy) are available in JS standart library, though they do not chain so well
function groupBy(array, selector) {
return array.reduce((res,curr) => {
const key = selector(curr)
if({}.hasOwnProperty.call(res, key)) {
res[key].push(curr)
} else {
res[key] = [curr]
}
return res
}, {})
}
function minBy(array, selector) {
return Math.min(...array.map(selector))
}
const groupedbyRow = groupBy(Object.entries(data), ([key, element]) => element.row)
const remappedValues = Object.values(groupedbyRow).flatMap(list => {
const smallestCol = minBy(list, ([key, val]) => val.col)
return list.map(([key, val]) => [key, {...val, ind: val.col, col: smallestCol}])
})
const res = Object.fromEntries(remappedValues)
Use the for...in statement iterates, as #Ravenous mentioned
const smallColl = currentRow => {
let small;
for (let key in data) {
const { col, row } = data[key];
if (row !== currentRow) continue;
if (!small || col < small) small = col;
}
return small;
};
// another method
const smallColl = currentRow =>
Math.min(
...Object.values(data)
.filter(({ row }) => row === currentRow)
.map(({ col }) => col)
);
for (let key in data) {
const { col, row } = data[key];
data[key].ind = col;
data[key].col = smallColl(row);
}
for better performance, use memoization:
for (let key in data) {
const colMin = {};
const { col, row } = data[key];
data[key].ind = col;
const min = colMin[row] || smallColl(row);
data[key].col = min;
if (colMin[row]) colMin[row] = min;
}

Array.reduce on a multidimensional array to array of objects

In my poker app I have an array of hands, each hand being array of randomly selected card objects with value and suit:
[ [ { value: 5, suit: 's' },
{ value: 4, suit: 's' },
{ value: 6, suit: 'c' },
{ value: 11, suit: 'd' },
{ value: 12, suit: 'c' } ],
[ { value: 9, suit: 'd' },
{ value: 12, suit: 'h' },
{ value: 8, suit: 'c' },
{ value: 12, suit: 's' },
{ value: 2, suit: 's' } ],
[ { value: 4, suit: 'h' },
{ value: 6, suit: 's' },
{ value: 10, suit: 'c' },
{ value: 3, suit: 'd' },
{ value: 7, suit: 'd' } ] ]
To prepare the hands for evaluation I want to use Array.reduce to return an array of hand objects. So the output would be:
[
{
values: [5, 4, 6, 11, 12],
suits: ['s', 's', 'c', 'd', 'c']
},
{
values: [9, 12, 8, 12, 2],
suits: ['d', 'h', 'c', 's', 's']
},
{
values: [4, 6, 10, 3, 7],
suits: ['h', 's', 'c', 'd', 'd']
}
]
I tried implementing this with nested forEach's, but its failing and I don't know why. I have two console.log's within which output as expected, but in the end hands is identical to the input.
let temp = []
hands.forEach((el) => {
temp = el
el = {}
el.values = []
el.suits = []
console.log(el) //expected output
temp.forEach((obj) => {
el.values.push(obj.value)
el.suits.push(obj.suit)
console.log(el) //expected output
})
})
console.log(hands) //same as original
You have to be thinking about the shape of your input data (DATA) and output (DATA')
Note 1:1 relationship between HAND and HAND' meaning we will use Array.prototype.map for one transformation. On the other hand, CARD has a N:1 relationship with HAND' meaing we will use Array.prototype.reduce for that transformation
So keep in mind while we're working, we will be doing a map and a reduce
const data =
[ [ { value: 5, suit: 's' },
{ value: 4, suit: 's' },
{ value: 6, suit: 'c' },
{ value: 11, suit: 'd' },
{ value: 12, suit: 'c' } ],
[ { value: 9, suit: 'd' },
{ value: 12, suit: 'h' },
{ value: 8, suit: 'c' },
{ value: 12, suit: 's' },
{ value: 2, suit: 's' } ],
[ { value: 4, suit: 'h' },
{ value: 6, suit: 's' },
{ value: 10, suit: 'c' },
{ value: 3, suit: 'd' },
{ value: 7, suit: 'd' } ] ]
let output =
data.map(cards =>
cards.reduce(({values, suits}, {value, suit}) => ({
values: [...values, value],
suits: [...suits, suit]
}), {values: [], suits: []}))
console.log(output)
Now of course that looks a little dense so it would be nice if we could dial down the complexity a bit. By making some curried adapters for map and reduce we can express a function that performs your transformation quite nicely
const data =
[ [ { value: 5, suit: 's' },
{ value: 4, suit: 's' },
{ value: 6, suit: 'c' },
{ value: 11, suit: 'd' },
{ value: 12, suit: 'c' } ],
[ { value: 9, suit: 'd' },
{ value: 12, suit: 'h' },
{ value: 8, suit: 'c' },
{ value: 12, suit: 's' },
{ value: 2, suit: 's' } ],
[ { value: 4, suit: 'h' },
{ value: 6, suit: 's' },
{ value: 10, suit: 'c' },
{ value: 3, suit: 'd' },
{ value: 7, suit: 'd' } ] ]
const map = f => xs => xs.map(f)
const reduce = f => y => xs => xs.reduce(f, y)
const handAppendCard = ({values, suits}, {value, suit}) => ({
values: [...values, value],
suits: [...suits, suit]
})
const makeHands =
map (reduce (handAppendCard) ({values:[], suits:[]}))
let output = makeHands (data)
console.log(output)
That's just one way to approach the problem. I hope you were able to learn something from it ^_^
There you go - a solution using nested Array.prototype.reduce functions:
var array=[[{value:5,suit:'s'},{value:4,suit:'s'},{value:6,suit:'c'},{value:11,suit:'d'},{value:12,suit:'c'}],[{value:9,suit:'d'},{value:12,suit:'h'},{value:8,suit:'c'},{value:12,suit:'s'},{value:2,suit:'s'}],[{value:4,suit:'h'},{value:6,suit:'s'},{value:10,suit:'c'},{value:3,suit:'d'},{value:7,suit:'d'}]];
var result = array.reduce(function(p, c) {
p.push(c.reduce(function(a, b) {
a.values.push(b.value);
a.suits.push(b.suit);
return a;
}, {values: [],suits: []}));
return p;
},[]);
console.log(result);
.as-console-wrapper {top: 0;max-height: 100%!important;}
You can use reduce flat to extract nested arrays. If you pass Infinity, no matter how deep they are, they'll be extracted.
const result = flatten([1,2,[3,4],[5,6,7]]) // result: [1,2,3,4,5,6,7]
Everything becomes one-dimensional array
Here's a simple solution with string concat and reduce. You could try something like:
var reduced = [];
//here a is your initial array
for(var i=0; i<a.length;i++){
reduced.push(a[i].reduce(function(prev,curr){
var obj={value:prev.value+','+curr.value,suit:prev.suit+','+curr.suit};return obj}));
}
console.log(reduced)
EDIT: As per #Barmar comment this returns string. If you want an array you could do:
for(var i=0; i<a.length;i++){
var tempElm =a[i].reduce(function(prev,curr) {
var obj= {value:prev.value+','+curr.value,suit:prev.suit+','+curr.suit};return obj});
tempElm['value'] = tempElm['value'].split();
tempElm['suit']= tempElm['suit'].split();
reduced.push(tempElm);
}
console.log(reduced)
EDIT 2: With the fair criticism for the above fix (which adds an over head of converting string to array) You could directly create array instead as follows:
var reduced = [];
for(var i=0; i<a.length;i++){
var valArray = []; var suitArray=[];
var tempElm = a[i].reduce(function(prev,curr) {
valArray.push(curr.value);suitArray.push(curr.suit);
var obj= {value:valArray,suit:suitArray};
return obj;
},null);
console.log(reduced)

Transforming JSON based on inputs and outputs

Following is an object array that has a value v, its input i and output o.
var data = [
{
v: 1,
i: [],
o: [1, 2, 3]
},
{
v: 2,
i: [2],
o: [4, 5, 6]
]
},
{
v: 3,
i: [1, 4],
o: [7, 8]
},
{
v: 4,
i: [],
o: [3]
}
]
The final JSON structure is created by checking the input and outputs of each v, i.e. the parent child relations...
Final JSON structure..
[
{
v: 1,
children: [
{
v: 2
},
{
v: 3
}
]
},
{
v: 4
}
]
I tried by the following code, but it's not transforming the object array properly...
function checkForOutputs(outputs, groupedValueChainEntityLists) {
for (var i = 0; i < outputs.length; i++) {
for (var j = 0; j < groupedValueChainEntityLists[j].inputs.length; j++) {
var val_Chain = groupedValueChainEntityLists[j].inputs.map((item) => {
if (outputs[i].o === item.o) {
return groupedValueChainEntityLists[j];
}
});
return val_Chain;
}
}
}
function constructValueChainRelations(data) {
var valueChainArray = new Array();
var result = data.map((item) => {
if (item.i.length === 0) {
valueChainArray.push(item);
return checkForOutputs(item.o, data);
}
});
console.log(result);
}
I think that you are making this too difficult. Simply map the values.
var data = [{
v: 1,
i: [],
o: [1, 2, 3]
}, {
v: 2,
i: [2],
o: [4, 5, 6]
}, {
v: 3,
i: [1, 4],
o: [7, 8]
}, {
v: 4,
i: [],
o: [3]
}];
function transform(verticies, idProp, childProp) {
return verticies.map(function(vertex) {
return {
v: vertex[idProp],
children: vertex[childProp].filter(function(childVertex) {
return childVertex !== vertex[idProp];
}).map(function(childVertex) {
return {
v: childVertex
};
})
}
});
}
var transformed = transform(data, 'v', 'o');
document.body.innerHTML = '<pre>' + JSON.stringify(transformed, null, 4) + '</pre>';
Result
[{
"v": 1,
"children": [{
"v": 2
}, {
"v": 3
}]
}, {
"v": 2,
"children": [{
"v": 4
}, {
"v": 5
}, {
"v": 6
}]
}, {
"v": 3,
"children": [{
"v": 7
}, {
"v": 8
}]
}, {
"v": 4,
"children": [{
"v": 3
}]
}]
You could use some loops and a look up mechanism with this.
var data = [{ v: 1, i: [], o: [1, 2, 3] }, { v: 2, i: [2], o: [4, 5, 6] }, { v: 3, i: [1, 4], o: [7, 8] }, { v: 4, i: [], o: [3] }],
result = [];
data.forEach(function (a) {
if (!this[a.v]) {
this[a.v] = { v: a.v, children: [] };
result.push(this[a.v]);
}
a.o.forEach(function (b) {
var k = a.v + '|' + b;
if (a.v !== b && !this[k]) {
this[k] = { v: b };
this[a.v].children.push(this[k]);
}
}, this);
}, {});
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
Here's another way that is working..
Sample Code
function populateChildrenRecursively(outputTypeId, valueChainEntities, parentValueChainEntity) {
for (var i = 0; i < valueChainEntities.length; i++) {
if (valueChainEntities[i].valueChainEntity.valueChainEntityId != parentValueChainEntity.valueChainEntity.valueChainEntityId && hasInput(outputTypeId, valueChainEntities[i].inputs)) {
parentValueChainEntity.valueChainEntity.items.push(valueChainEntities[i]);
if (valueChainEntities[i].outputs.length > 0) {
valueChainEntities[i].valueChainEntity.items = [];
for (var j = 0; j < valueChainEntities[i].outputs.length; j++) {
populateChildrenRecursively(valueChainEntities[i].outputs[j].outputTypeId, valueChainEntities, valueChainEntities[i]);
}
}
}
}
}
JSON Conversion

Categories

Resources