Dynamic conditions of array for javascript - javascript

const conditionalArray = [
{ name: "line", condition: ">=", value: 5 },
{ name: "revene", condition: "in", value: 6 },
];
const dataTofilter = [
{ line: 3, revene: 4, sale: 3, fridge: "lg" },
{ line: 6, revene: 3, sale: 2, fridge: "samsung" },
];
I have these 2 arrays one is having conditions ( many more can be there) and second is to filter
Final result should be [{ line: 6, revene: 3, sale: 2, fridge: "samsung" }]

Something along the lines of:
const conditionalArray = [
{ name: "line", condition: ">=", value: 5 },
{ name: "revene", condition: "<", value: 6 },
];
const dataTofilter = [
{ line: 3, revene: 4, sale: 3, fridge: "lg" },
{ line: 6, revene: 3, sale: 2, fridge: "samsung" },
];
const conditions = {
'<': (x, y) => x < y,
'<=': (x, y) => x <= y,
'>': (x, y) => x > y,
'>=': (x, y) => x >= y,
};
const result = dataTofilter.filter(data => {
for (const el of conditionalArray) {
if (!conditions[el.condition](data[el.name], el.value)) {
return false;
}
}
return true;
});
console.log(result);

const newData = dataTofilter.filter((item) => {
const passedConditions = [];
conditionalArray.forEach((cond) => {
switch(cond.condition){
case ">":
passedCondition.push(item[cond.name] > cond.value);
break;
case ..... //other conditional checks
}
}); //end forEach
return Arrays.asList(passedCondition).contains(false) === false;
});
If you don't want to use eval you can do something like this. We loop through all the data first and use filter to only return values that pass our conditional check.
We then loop trough the conditional array testing each condition on the single item and save the value to passedConditions. You will need to add the rest of the conditions.
Finally we return wether the passedCondtions array did not contain a false value. This would mean we passed all the conditional checks.

I'm really not a big fan of it. But I don't see another way as eval() to make the condition work as an actual operator without doing plenty of manual checks. Also the following solution assumes that every item in dataToFilter has a corresponding item in conditionalArray.
const conditionalArray = [
{ name: "line", condition: ">=", value: 5 },
{ name: "revene", condition: "<", value: 6 },
];
const dataTofilter = [
{ line: 3, revene: 4, sale: 3, fridge: "lg" },
{ line: 6, revene: 3, sale: 2, fridge: "samsung" },
];
const result = dataTofilter.filter((item, i) =>
eval(
`${item[conditionalArray[i].name]}${conditionalArray[i].condition}${
conditionalArray[i].value
}`
)
);
console.log(result);

Related

How to derive new objects value from two different arrays in Javascript

I have two arrays as below,
var day1 = [{id: 1, type:"20H", cases: 30, fail: 5},
{id: 1, type:"12C", cases: 10, fail: 3},
{id: 1, type:"4B", cases: 20, fail: 8}];
var day5 = [{id: 5, type:"12C" ,cases: 5, fail: 2},
{id: 5, type:"4B", cases: 12, fail: 3},
{id: 5, type:"20H", cases: 20, fail: 6}];
Expecting the below result of array,
var result = [{id: 1, type:"20H", caseCount: 30, failCount: 5, difference: 10, diffPercentage: 50.00},
{id: 1, type:"12C", caseCount: 10, failCount: 3, difference: 5, diffPercentage: 100.00},
{id: 1, type:"4B", caseCount: 20, failCount: 8, difference: 8, diffPercentage: 66.66}];
The logic of difference and diffPercentage as below,
Here, I am not getting how to get the matched value between two arrays and proceed.
if(day1.type === day5.type){
difference = day1.cases - day5.cases;//Here, 30-20 = 10
diffPercentage = ((day1.cases - day5.cases)/(day5.cases)*100).toFixed(2);// 10/20 * 100 = 50.00
}
Tried the following,
result = [];
day1.forEach(function(day1Items, idx){
var day5Items = day5[idx];
var outputElements = {};
if(day1Items && day5Items){
if(day1Items.type == day5Items.type)){ //Here, I am not getting how to get the matched value between two array and proceed.
console.log("Inside if block2"); //it is coming here, because, the if condition going index wise check
outputElements.id = day1Items.id;
outputElements.type = day1Items.type;
outputElements.caseCount = day1Items.cases;
outputElements.failCount = day1Items.fail;
outputElements.difference = day1Items.cases - day5Items.cases;//Here, I need to get the cases value respective type matched
outputElements.diffPercentage = ((day1.cases - day5.cases)/(day5.cases)*100).toFixed(2);
result.push(outputElements);
}
}
});
console.log(result);
You can use a Map to achieve this.
var day1 = [
{ id: 1, type: "20H", cases: 30, fail: 5 },
{ id: 1, type: "12C", cases: 10, fail: 3 },
{ id: 1, type: "4B", cases: 20, fail: 8 }
];
var day5 = [
{ id: 5, type: "12C", cases: 5, fail: 2 },
{ id: 5, type: "4B", cases: 12, fail: 3 },
{ id: 5, type: "20H", cases: 20, fail: 6 }
];
function merge(arr1, arr2) {
const hashMap = new Map();
arr1.forEach((elem) => {
const elemClone = {
...elem,
failCount: elem.fail,
caseCount: elem.cases
};
delete elemClone.fail;
delete elemClone.cases;
hashMap.set(elem.type, elemClone);
});
arr2.forEach((elem) => {
if (hashMap.has(elem.type)) {
const difference = Math.abs(elem.cases -
hashMap.get(elem.type).caseCount);
const diffPercentage = Number(parseFloat((difference / elem.cases)
* 100).toFixed(2));
hashMap.set(elem.type, {
...hashMap.get(elem.type),
difference,
diffPercentage
});
} else {
hashMap.set(elem.type, elem);
}
});
return Array.from(hashMap.values());
}
console.log(merge(day1, day5));
This should work:
var day1 = [{id: 1, type:"20H", cases: 30, fail: 5},
{id: 1, type:"12C", cases: 10, fail: 3},
{id: 1, type:"4B", cases: 20, fail: 8}];
var day5 = [{id: 5, type:"12C" ,cases: 5, fail: 2},
{id: 5, type:"4B", cases: 12, fail: 3},
{id: 5, type:"20H", cases: 20, fail: 6}];
let result = []
day1.forEach(d1 => {
const day5Item = day5.find(d5 => d5.type === d1.type);
if(day5Item){
let difference = d1.cases - day5Item.cases;
let diffPercentage = (difference/day5Item.cases*100).toFixed(2)
result.push({
id:d1.id,type:d1.type,caseCount:d1.cases,failCount:d1.fail,difference,diffPercentage
})
}
});
console.log(result);
Here is the corrected version of your code.
The issue with your code was, you were looping through day1 array with array.forEach and by making use of that index you were selecting matching node from day5 array. Thats worng, This will work only if the arays are in the same order with type. Rather that selecting with var day5Items = day5[idx]; you have to select the node from day5 using Array.find and checking with type. Thil will give you the desired node.
Also there was an error in calculating diffPercentage
Instead of outputElements.diffPercentage = ((day1.cases - day5.cases)/(day5.cases)*100).toFixed(2); It should be outputElements.diffPercentage = ((day1Items.cases - day5Items.cases)/(day5Items.cases)*100).toFixed(2);. day1 and day5 are arrays. You cannot access day1.cases or day5.cases instead it should be day1Items.cases and day5Items.cases
var day1 = [
{ id: 1, type: "20H", cases: 30, fail: 5 },
{ id: 1, type: "12C", cases: 10, fail: 3 },
{ id: 1, type: "4B", cases: 20, fail: 8 }
];
var day5 = [
{ id: 5, type: "12C", cases: 5, fail: 2 },
{ id: 5, type: "4B", cases: 12, fail: 3 },
{ id: 5, type: "20H", cases: 20, fail: 6 },
];
const result = [];
day1.forEach(function(day1Items, idx){
// You cannot simply select with index.
// Instead make use of `Array.find` to select the node from day5 Array with the condition
// var day5Items = day5[idx];
var day5Items = day5.find((node) => node.type === day1Items.type)
var outputElements = {};
if(day1Items && day5Items){
if(day1Items.type == day5Items.type){ //Here, I am not getting how to get the matched value between two array and proceed.
outputElements.id = day1Items.id;
outputElements.type = day1Items.type;
outputElements.caseCount = day1Items.cases;
outputElements.failCount = day1Items.fail;
outputElements.difference = day1Items.cases - day5Items.cases;//Here, I need to get the cases value respective type matched
// Calculation was wrong here
outputElements.diffPercentage = ((day1Items.cases - day5Items.cases)/(day5Items.cases)*100).toFixed(2);
result.push(outputElements);
}
}
});
console.log(result);
Simplified Method
Use Array.reduce
var day1 = [
{ id: 1, type: "20H", cases: 30, fail: 5 },
{ id: 1, type: "12C", cases: 10, fail: 3 },
{ id: 1, type: "4B", cases: 20, fail: 8 },
{ id: 1, type: "49B", cases: 20, fail: 8 }
];;
var day5 = [
{ id: 5, type: "12C", cases: 5, fail: 2 },
{ id: 5, type: "4B", cases: 12, fail: 3 },
{ id: 5, type: "20H", cases: 20, fail: 6 },
];
const result = day1.reduce((acc, curr) => {
const insertNode = { id: curr.id, type: curr.type, caseCount: curr.cases, failCount: curr.fail };
const d5Node = day5.find((node) => node.type === curr.type);
if (d5Node) {
insertNode.difference = curr.cases - d5Node.cases;
insertNode.diffPercentage = (insertNode.difference / d5Node.cases * 100).toFixed(2);
} else {
// Handle the calculation logic here
insertNode.difference = curr.cases;
insertNode.diffPercentage = insertNode.difference * 100;
}
acc.push(insertNode);
return acc;
}, []);
console.log(result);

Return multiple sets of aggregated IDs from an array of objects

Without using JQuery or other plugins I'd like to look for instances of matching attributes from an array of objects.
For example - in this array of objects there are two attributes that appear across more than one object (A and C with 3 and 2 values respectively).
The result format I'm after would be an array of arrays containing the IDs of the objects in question (see the result line). I've tried various types of filters but to no avail.
let data = [
{ id: 1, attribute: "A" },
{ id: 2, attribute: "b" },
{ id: 3, attribute: "C" },
{ id: 4, attribute: "A" },
{ id: 5, attribute: "e" },
{ id: 6, attribute: "C" },
{ id: 7, attribute: "g" },
{ id: 8, attribute: "h" },
{ id: 9, attribute: "A" }
];
let result = [
[1, 4, 9],
[3, 6]
];
You can first use array reduce() method to group objects by attribute key and the get the required ids in a group like:
let data = [
{id:1, attribute:"A"}
, {id:2, attribute:"b"}
, {id:3, attribute:"C"}
, {id:4, attribute:"A"}
, {id:5, attribute:"e"}
, {id:6, attribute:"C"}
, {id:7, attribute:"g"}
, {id:8, attribute:"h"}
, {id:9, attribute:"A"}];
let result = Object.values(data.reduce((o, obj) => {
o[obj.attribute] = (o[obj.attribute] || []).concat(obj);
return o;
}, {})).filter(({length}) => length>1).map(x=> x.map(({id}) => id));
console.log(result)
Please use Array.reduce to make simple.
let data = [
{ id: 1, attribute: "A" },
{ id: 2, attribute: "b" },
{ id: 3, attribute: "C" },
{ id: 4, attribute: "A" },
{ id: 5, attribute: "e" },
{ id: 6, attribute: "C" },
{ id: 7, attribute: "g" },
{ id: 8, attribute: "h" },
{ id: 9, attribute: "A" }
];
let result = [
[1, 4, 9],
[3, 6]
];
let results = data.reduce((x, {id, attribute}) => {
if(!x[attribute]) x[attribute] = []
x[attribute].push(id)
return x
},{})
let finalResult = []
for( let key in results) {
const value = results[key]
if(value.length > 1) finalResult.push(value)
}
console.log(finalResult)

Remove all keys that have the same value in array

I need to remove all keys except last inserted that have the same value on key id_ask in array but I'm learning javascript and I still do not know how to do this.
jQuery(function()
{
let arr = []
let q = []
$("body").on('click', '.link_resposta', function(event)
{
event.preventDefault();
/* Act on the event */
let id_poll = $(this).data("idpesquisa")
let id_ask = $(this).data("idpergunta")
let id_anwser = $(this).children("li").data("idresposta")
let q = {
id_poll,
id_ask,
id_anwser
}
arr.push(q)
console.log(arr)
});
});
Using a combination of Set, Array.reverse() and Array.map we can solve this easily.
We first use the Set and we map our source array in, just feeding the id_ask field. From that we get an array of unique id_ask.
We then map the unique id_ask array and for each id_ask we call a find() on the source array in reverse.
Comments inline.
const sampleArray = [
{
id: 1,
id_ask: 2,
id_answer: 3
},
{
id: 2,
id_ask: 2,
id_answer: 5
},
{
id: 3,
id_ask: 3,
id_answer: 3
},
{
id: 4,
id_ask: 3,
id_answer: 1
},
{
id: 5,
id_ask: 4,
id_answer: 3
}
];
// Create a unique Set of Ask ID
const uniqueAskId = [...new Set(sampleArray.map(e => e.id_ask))];
console.log(uniqueAskId);
// Use Map and Reverse to get last item.
const r = uniqueAskId.map(uid => sampleArray.reverse().find(ask => ask.id_ask === uid));
console.log(r);
Here it is as a single statement:
const sampleArray = [
{
id: 1,
id_ask: 2,
id_answer: 3
},
{
id: 2,
id_ask: 2,
id_answer: 5
},
{
id: 3,
id_ask: 3,
id_answer: 3
},
{
id: 4,
id_ask: 3,
id_answer: 1
},
{
id: 5,
id_ask: 4,
id_answer: 3
}
];
// put together in a single statement.
const result = [...new Set(sampleArray.map(e => e.id_ask))]
.map(uid => sampleArray.reverse().find(ask => ask.id_ask === uid));
console.log(result);
NOTE: For large datasets it would obviously be more efficient to call the reverse() one time before you use.
const revArray = myArray.reverse();
const resultArray = [...new Set(revArray.map(e => e.id_ask))]
.map(uid => revArray.reverse().find(ask => ask.id_ask === uid));

Reduce method - cannot get two seperate results

I've got an array like that:
const arr = [
[{rivals: ['player1','player2'], winner: "player1", player1Scored: 2, player2Scored: 1}],
[{rivals: ['player1','player3'], winner: "none", player1Scored: 2, player3Scored: 2}],
[{rivals: ['player2','player3'], winner: "player3", player2Scored: 1, player3Scored: 3}],
[{rivals: ['player1','player4'], winner: "none", player1Scored: 1, player4Scored: 1}]
]
I need to count scored points of every player, so it'll look like that:
{player1Scored: 5, player2Scored: 2, player3Scored: 5, player4Scored:1}
I tried this:
let scoreResult = arr.reduce((result, {0: obj}) => {
obj.rivals.forEach(rival => result[`${rival}Scored`] = result[`${rival}Scored`] || 0);
obj.rivals.forEach(rival => result[`${rival}Scored`] += obj.player1Scored)
return result;
}, {});
My mistake is that I'm asigning points of one player to two of them but cannot solve that.
Thank you for your help
To answer the problem, you have nested arrays with a single object. You could take a destructuring with the array and take the firts item as object.
Then take the names from rivals array and add the scores.
const
array = [[{ rivals: ['player1', 'player2'], winner: "player1", player1Scored: 2, player2Scored: 1 }], [{ rivals: ['player1', 'player3'], winner: "none", player1Scored: 2, player3Scored: 2 }], [{ rivals: ['player2', 'player3'], winner: "player3", player2Scored: 1, player3Scored: 3 }], [{ rivals: ['player1', 'player4'], winner: "none", player1Scored: 1, player4Scored: 1 }]],
result = array.reduce((r, [o]) => {
o.rivals.forEach(k => r[k + 'Scored'] = (r[k + 'Scored'] || 0) + o[k + 'Scored']);
return r;
}, Object.create(null));
console.log(result);
try this
scores = {}
arr.map(s => s[0]).forEach(s => {
s.rivals.forEach(r => {
scores[r] = scores[r] || 0;
scores[r] += s[`${r}Scored`]
})
})

Sum object values with the same key

I am kinda new to javascript and today I encountered problem. Thing is, I have an array of amount of people visited every day by each hour (as you can see bellow). And I would like to find out most popular hour of day. My plan was to create a map where key is index of hour (0, 1, 2, 3, 4, 5...) and value is sum of all people who visited across all days on that hour. Problem is I'm not able to do that with my JS knowledge. Can someone give me a direction how to approach this problem? Thank you very much.
[
{date: "25.05.2018",
value: {
1: 209
2: 123
3: 890
.
.
24: 789
}
},
{date: "26.05.2018",
value: {
1: 280
2: 398
3: 450
.
.
24: 76
}
}
]
My JAVA like solution:
const { data: { data: [{ values }] } } = insightsData;
const timesMap = new Map();
values.forEach(item => {
Object.entries(item.value).forEach(([key, value]) => {
const timeValue = timesMap.get(key);
if (timeValue) {
timesMap.set(key, timeValue + value);
} else {
timesMap.set(key, value);
}
});
});
You could use Array#reduce with Array#map if you have arrays with the same length.
reduce takes an array as accumulator r and uses a logical OR || with a zero as value if an item does not exist in the accumulator.
var counts = [{ date: "26.05.2018", value: [125, 100, 200] }, { date: "27.05.2018", value: [5, 6, 7] }, { date: "28.05.2018", value: [3, 4, 5] }],
result = counts.reduce(
(r, { value }) => value.map((v, i) => (r[i] || 0) + v),
[]
);
console.log(result);
With objects as value properties.
var counts = [{ date: "26.05.2018", value: { 0: 125, 1: 100, 2: 200 } }, { date: "27.05.2018", value: { 0: 5, 1: 6, 2: 7 } }, { date: "28.05.2018", value: { 0: 3, 1: 4, 2: 5 } }],
result = counts.reduce(
(r, { value }) => Object
.entries(value)
.reduce((s, [k, v]) => {
s[k] = (s[k] || 0) + v;
return s;
}, r),
{}
);
console.log(result);
You can do iteratre over the values and add them to sum, like this
const data = {date: "26.05.2018",
value: [
125,
100,
200,
]
}
let sum = 0;
Object.values(data.value).forEach(elem => sum = sum + elem)
console.log(sum)
https://jsfiddle.net/5wwzn4yt/

Categories

Resources