I have this source table data:
And I would like to get data parsed like this (grouped by row):
I grouped source table into an array of objects.
This is my array:
[ { rowId: 4, colId: 10 } { rowId: 4, colId: 11 } .... ]
Now I would like to get an array of parsed objects..
How can I do this?
I parsed the array with a for loop but I got some error when I create the new array of objects..
My code:
for (var i=0; i<tableArray.length; i++) {
if (tableArray[i].rowId != rowLast) {
bar = true;
var row = tableArray[i].rowId;
var start = tableArray[i].colId;
}
if ((bar)&&(tableArray[i].colId != colLast)) {
var end = tableArray[i].colId;
tab = { row: row, start: start, end: end }
newTableArray.push(tab);
bar = false;
}
rowLast = tableArray[i].rowId;
colLast = tableArray[i].colId;
}
Help! I'm a bit confused in loop :(
Many thanks.
You could group the elements and use an object for the last values. This solution needs sorted data.
var array = [{ rowId: 4, colId: 10 }, { rowId: 4, colId: 11 }, { rowId: 4, colId: 12 }, { rowId: 4, colId: 20 }, { rowId: 4, colId: 21 }, { rowId: 6, colId: 6 }, { rowId: 6, colId: 7 }, { rowId: 6, colId: 8 }, { rowId: 7, colId: 12 }, ],
group = [];
array.forEach(function (a, i) {
if (!i || // group changes if first object i = 0
this.last.row !== a.rowId || // or different rowId
this.last.end + 1 !== a.colId // or not in sequence
) {
this.last = { row: a.rowId, start: a.colId, end: a.colId };
group.push(this.last);
}
this.last.end = a.colId;
}, {});
console.log(group);
I would rather write a function to generate the new array, I hope the comments explain the thought process behind it:
function transform(array) {
var output = [];
// initiates the first object you want in your output array
// with the row and colId of the first object from the input array
var obj = {
row: array[0].row,
start: array[0].colId,
end: array[0].colId
};
// Loop starts at 1 instead of 0 because we used the first object array[0] already
for (var i = 1; i < array.length; i++) {
var current = array[i];
// if the current objects row is still the same,
// AND the colId is the next colId (meaning no spare cols between)
// set the new objects end to this colId
if(obj.row === current.row && (current.colId - obj.end) === 1 ){
obj.end = current.colId;
}
// when the row does not match, add the object to the output array and
// re-innitiate it with the current objects row and colId
else {
output.push(obj);
obj.row = current.row;
obj.start = current.colId;
obj.end = current.colId;
}
}
// Once the loop is done, add the last remaining object to the output array
output.push(obj);
return output;
}
Related
I have mapsOrder array and mapsData array of objects:
let mapsOrder = [1,2,1,3];
let mapData = [
{
id: 1,
gates: [
{
toId: 2,
coords: {
x: 2,
y: 42
}
},
{
toId: 3,
coords: {
x: 9,
y: 4
}
}
]
},
{
id: 2,
gates: [
{
toId: 1,
coords: {
x: 6,
y: 5
}
}
]
},
{
id: 3,
gates: [
{
toId: 1,
coords: {
x: 2,
y: 1
}
}
]
}
]
What I want to achieve is in loop basing on mapsOrder where mapsOrder array values are ids in mapData, designate gates to next map.
So we have loop that iterate 4 times and when:
loop index is 1 current map is 1 next map is 2 and gates to next are coords: { x: 2, y: 42 }
loop index is 2 current map is 2 next map is 1 and gates to next are coords: { x: 6, y: 5 }
loop index is 3 current map is 1 next map is 3 and gates to next are coords: { x: 9, y: 4 }
loop index is 4 current map is 3 next map is 1 and gates to next are coords: { x: 2, y: 1 }
last loop iteration see next map as first of mapsOrder array. I tried to do it myself by first determineting the id of next map like so:
for(let i = 0; i < mapsOrder.length; i++) {
let nextMap;
let currentMapId = mapData[mapsOrder[i] - 1].id;
if(i === mapsOrder.length - 1) {
nextMap = mapData[0].id
} else {
nextMapId = mapData[mapsOrder[i]].id;
}
console.log('Current map is: ', currentMapId, 'and the next map id is:', nextMapId)
console.log('break-----')
}
but this console incorrect ids, demo
If you don't care about the original array then just use shift to get the next gate (shift will remove the gate from the array thus the next gate will be available when the object is encountered again). Use find to find the object from the array:
let result = mapsOrder.map(id =>
mapData.find(o => o.id == id).gates.shift().coords
);
You may want to check if find actually finds something and the gates array contains something before using shift, here is a safer way:
let result = mapsOrder.map(id => {
let obj = mapData.find(o => o.id == id);
if(obj && obj.gates.length) { // if we found an object with the same id and that object still have gates
return obj.gates.shift().coords; // return the coords of the first gate and remove the gate from the array
} // otherwise, throw an error or something
});
No altering:
Instead of using shift from the previous example, we'll just use an object to track the gate index from the gates array:
let nextGateIndex = Object.create(null); // create a prototypeless object to track the next gate index for each object
let result = mapsOrder.map(id => {
let obj = mapData.find(o => o.id == id);
let index;
if(nextGateIndex[id] == undefined) {
index = 0;
} else {
index = nextGateIndex[id] + 1;
}
nextGateIndex[id] = index;
if(obj && index < obj.gates.length) {
return obj.gates[index].coords;
} // throw error or something
});
If follow your description your loop should look like. Seems that you wand to use id and toId but using array indexes. It can be a good idea to replace arrays with objects.
Demo
for(let i = 0; i < mapsOrder.length; i++) {
let nextMap;
let currentMapId = mapsOrder[i];
if(i === mapsOrder.length - 1) {
nextMapId = mapsOrder[0]
} else {
nextMapId = mapsOrder[i + 1];
}
let filteredMapData = mapData.filter(f => f.id == currentMapId);
let filteredGates = filteredMapData.length > 0 ? filteredMapData[0].gates.filter(f => f.toId == nextMapId) : [];
console.log('Current map is: ', currentMapId, 'and the next map id is:', nextMapId, 'gates:', filteredGates.length == 0 ? "no gates": filteredGates[0].coords)
console.log('break----')
}
I would recommend the filter() function for javascript arrays as it is super quick. This function will return an array filled with items from original matching some criteria (in this case, objects having the desired id).
for (let i = 0; i < mapsOrder.length; i++) {
console.log(mapData.filter(mapDataItem => mapDataItem.id === mapsOrder[i]))
}
I am trying to get my array of objects in the format that is needed to create a C3 bar chart but I am having trouble categorizing my data with JavaScript. Below is my JavaScript code.
data = [
{Service:"Army",Permanent:20,Itinerant:754,Region:"Western"},
{Service:"Air Force",Permanent:100,Itinerant:2,Region:"Eastern"},
{Service:"Army",Permanent:10,Itinerant:7,Region:"Western"},
{Service:"Air Force",Permanent:30,Itinerant:2,Region:"Eastern"}
]
var sumAry=[];
for (var x=0; x<data.length; x++){
var val =sumAry.indexOf(data[x].Service);
if(val === -1){
var permanent += data[x].Permanent;
sumAry.push(data[x].Service, permanent);
}else{
console.log("IN");
}
}
https://codepen.io/isogunro/pen/GYRKqE?editors=0012
The C3 chart looks for a data in the structure/format shown below:
['Army', 30],
['Airorce', 130],
['Navy', 190],
['Army1', 70],
['Airorce2', 130],
['Navy3', 100]
For each of the values, the 'Permanent' property will be added up to get the number part of the array. It becomes an aggregate of all the information.
Assuming the number in the preferred format comes from the Permanent property, you could use Array.map to transform your dataset.
var data = [{
Service: "Army",
Permanent: 654,
Itinerant: 754,
Region: "Western"
},
{
Service: "Air Force",
Permanent: 9,
Itinerant: 2,
Region: "Eastern"
},
{
Service: "Army",
Permanent: 6,
Itinerant: 7,
Region: "Western"
},
{
Service: "Air Force",
Permanent: 9,
Itinerant: 2,
Region: "Eastern"
}
];
var aggregates = data.map(function (o) {
// map original objects to new ones with zeroed-out data
return {
Service: o.Service,
Permanent: 0,
}
}).filter(function (o, index, a) {
// filter the list to have only unique `Service` properties
var service = o.Service;
var i;
for (i = 0; i < a.length; i += 1) {
if (a[i].Service === service) {
// break out of the loop when the first matching `Service` is found.
break;
}
}
// `i` is now the index of the first matching `Service`.
// if it's the first occurrence of that `Service`, keep it, otherwise ditch it.
return i === index;
});
data.forEach(function (o) {
// loop through the aggregate list and get the matching `Service` property.
var agg = aggregates.filter(function (p) {
return o.Service === p.Service;
})[0]; // first element is the match.
// sum the `Permanent` properties.
agg.Permanent += o.Permanent;
});
// now that the data has been aggregated, transform it into the needed structure.
var c3data = aggregates.map(function (d) {
return [d.Service, d.Permanent];
});
console.log(JSON.stringify(c3data));
I have a button that has a function called clickNext(). Whenever that button is clicked, it increments the index position (scope.selected) on an array called 'arr1'.
<button type="button" class="right-btn col-xs-6" role="menuitem" ng-click="clickNext()">Next</button>
.
function clickNext()
{
scope.selected = (scope.selected + 1) % length;
}
arr1 = [
{apple: 1 , tango},
{banana: 3, kappa},
{orange:5, alpha},
{apple: 8 , beta},
{grape: 10 , sigma}
]
Problem
I have an identical array to arr1 called 'arr2'. What I'm trying to do is have the clickNext() increment to the next index position based on the arr2 array instead of the arr1 array.
Right now, the clickNext function still increments in the order of the arr1 array. For example, if I were to click the button, it would start on orange:5 then move to apple 8, etc.
arr2 = [
{orange:5, alpha},
{apple: 8 , beta},
{banana: 3, kappa},
{grape: 10 , sigma},
{apple: 1 , tango}
]
What I have tried
My though process to accomplish this is to use the findIndex() function and match the arr2 item to the arr1 item. That doesn't work, but maybe I'm structuring it wrong?
clickNext(){
var oldIndex = filteredToXs(scope.selected);
scope.selected = oldIndex + 1;}
function filteredToXs( filteredIndex ) {
var firstArr = scope.arr1[ filteredIndex ];
var xsIndex = scope.arr2.findIndex( function(item) {
return item.trackingNumber === firstArr.trackingNumber;
} );
if( xsIndex >= 0 ) return xsIndex;
if( xsIndex === -1 ) return 0; // Default value
}
I hope I understood your question correctly. Please read my comments in the code sections as well.
I had to modify your source so I was able to create a fiddle for you.
HTML: I changed the click event and removed a css class that's not available
<button type="button" role="menuitem" onclick="clickNext();">Next</button>
Sampe Arrays:
They were containing invalid objects: I changed alpha, beta, tango, .. to a property. You can also define them as values.. this shouldn't matter:
var arr1 = [
{ apple: 1, tango: '' },
{ banana: 3, kappa: '' },
{ orange: 5, alpha: '' },
{ apple: 8, beta: '' },
{ grape: 10, sigma: '' }];
var arr2 = [
{ orange: 5, alpha: '' },
{ apple: 8, beta: '' },
{ banana: 3, kappa: '' },
{ grape: 10, sigma: '' },
{ apple: 1, tango: '' }];
Code:
var idx = 0; //save current index of array 2
function clickNext() {
idx++;
//I'm comparing the array objects using a string compare- this only works because you said 'I have an identical array'
//this may cause issues if you're objects are cross-referenced
var find = function(array, obj) { //lookup value from arr2 in arr1
for (var i=0, l=array.length;i<l;i++)
if (JSON.stringify(array[i]) == JSON.stringify(obj)) //adjust this line to your needs
return array[i];
}
var result = find(arr1, arr2[idx])
if (!result)
throw new Error('not found- your arrays are not identical or you run out of index');
console.log(result);
}
fiddle: https://jsfiddle.net/k50y8pp5/
I want to store all duplicates and unique values of suits and values of an array of Card objects. Each card has a suit and value property. The datastructure looks like this:
cards = [
{
suit: 'spades',
value : 4
},
{
suit: 'hearts',
value : 4
},
{
suit: 'spades',
value : 11
},
{
suit: 'spades',
value : 12
}
etc...
]
I'm trying to use array.reduce to check and store duplicates and unique values and suits for both, but am having trouble structuring the code.
Rules:
3-of-a-kind with different suits
4-card-run (with incrementing values) with same suits
Basically... I need to check each card's value and suits... and count the duplicates and uniques of values and suits. I'm struggling passing in an array of objects and using reduce on it.
Output: something like
melds : {
values: [4, 4, 4]
suits: [spades, hearts, diamonds]
},
runs : {
values: [11, 12, 13],
suits: ['spades', 'spades', 'spades']
}
Code:
function calculate(cards) {
var my_array = cards.reduce(function(prev_array, curr, index, array){
if (prev_array.duplicates.values.indexOf(curr) !== -1 || array.lastIndexOf(curr) !== index) {
prev_array.duplicates.values.push(curr);
} else {
prev_array.uniques.values.push(curr);
}
if (prev_array.duplicates.suits.indexOf(curr) !== -1 || array.lastIndexOf(curr) !== index) {
prev_array.uniques.suits.push(curr);
} else {
prev_array.duplicates.suits.push(curr);
}
return prev_array;
},
{
duplicates : {
values : [],
suits : []
},
uniques : {
values : [],
suits : []
}
}
);
return my_array;
}
Edit:
var values = [2, 3, 4, 5, 6, 6, 6];
var suits = ['spades', 'spades', 'spades', 'spades', 'diamonds', 'clubs', 'hearts'];
var test_values = potentialRunsAndMelds(values);
var test_suits = potentialRunsAndMelds(suits);
function potentialRunsAndMelds(array) {
var my_array = array.reduce(function(prev_array, curr, index, array){
if (prev_array.duplicates.indexOf(curr) !== -1 || array.lastIndexOf(curr) !== index) {
prev_array.duplicates.push(curr);
} else {
prev_array.uniques.push(curr);
}
return prev_array;
},
{
uniques : [],
duplicates : []
}
);
return my_array;
}
var values = [2, 3, 4, 5, 6, 6, 6];
var suits = ['spades', 'hearts', 'spades', 'spades', 'diamonds', 'clubs', 'spades'];
EDIT 2:
var runs = Object.keys(groups.suits).map(function (suit) {
var values = groups.suits[suit].sort();
var run = [];
for (var i = 0; i < values.length; i++) {
if (values[i+1] - values[i] === 1) {
if (run.indexOf(values[i+1]) === -1) {
run.push(values[i+1]);
}
if (run.indexOf(values[i]) === -1) {
run.push(values[i]);
}
}
}
if (run.length >= 4) return run;
});
Returns : [Array[4], undefined, undefined, undefined]
Where Array[4] is [2, 3, 4, 5]
How can I not return undefined?
I suppose I can just do:
runs = runs.filter(function (run) {
return run.length;
});
You might be trying to do too much in one reduce function; maybe try breaking this into steps? Anyway, having your reducer group by value and suit would simplify things.
var groups = cards.reduce(function (accumulator, card) {
// Group by value.
accumulator.values[card.value] = accumulator.values[card.value] || [];
accumulator.values[card.value].push(card.suit);
// Group by suit.
accumulator.suits[card.suit] = accumulator.suits[card.suit] || [];
accumulator.suits[card.suit].push(card.value);
return accumulator;
}, {values: {}, suits: {}});
Once you've done that, it's much easier to find melds and runs.
// Melds
var meldValues = Object.keys(groups.values).filter(function (value) {
// Check for duplicates in this array, if so inclined.
return groups.values[value].length >= 3;
});
// Runs
var runs = Object.keys(groups.suits).map(function (suit) {
var values = groups.suits[suit].sort();
// (1) iterate over values
// (2) append each value to current 'run' as long as it's consecutive
// (3) if not consecutive, start a new run. if duplicate, discard.
// (4) return all runs with length >= 4
});
OK, I don't gamble and maybe I quite don't understood what you want to achieve, but it seem to me, that you forgot to get property value from cards array.
You are passing the curr
if (prev_array.duplicates.values.indexOf(curr) !== -1 ...
But curr is card object.
cards = [
{
suit: 'spades',
value : 4
},
You should target suit and value like curr.suit and cur.value.
And because in JS, Objects cannot be easily compared so lastIndexOf(object) === indexOf(object) equals in all cases. and you need to check uniques Array if the value is'nt already there, because you cannot rely on lastIndexOf().
Also, because Objects cannot be easily compared, testing duplicity with Object is bad idea.
The first list in image shows what I have achieved after loop. Now I want sum of "pay" grouped by "organizationid" as shown in the second list.
I only need the second list to be saved in database but could not achieve it with JQuery.
How can I get the sum grouped by organizationid using jquery?
You can loop through the array and aggregate them to another object:
var input = [
{ oId: 1, pay: 10 },
{ oId: 1, pay: 10 },
{ oId: 2, pay: 20 },
{ oId: 2, pay: 20 },
{ oId: 3, pay: 30 }
];
var result = {} ;
for (var i = 0; i < input.length; i++) {
var rec = input[i];
if(result.hasOwnProperty(rec.oId))
result[rec.oId] += rec.pay;
else
result[rec.oId] = rec.pay;
}
return result;