My target is to create a key and append them in a DIV. My old function works good but I have made adjustments so I changed my code to a better one. The problem is, I cannot append anymore when the array is = 1.
This is my old code: (We can see here after being matched, they're appended to the DIV using keys. Even if length = 1, there's also a key to append on my div).
//just for demo....
var data = {
"data2": [{
"entryID": "1",
"player": "testing1",
"level": "3"
}, {
"entryID": "2",
"player": "testing2",
"level": "1"
}, {
"entryID": "3",
"player": "testing3",
"level": "2"
}, {
"entryID": "4",
"player": "testing4",
"level": "2"
}, {
"entryID": "5",
"player": "testing5",
"level": "1"
}, {
"entryID": "6",
"player": "testing6",
"level": "5"
}]
}
const combine = (source) => {
return source.reduce((acc, curr) => {
if (acc[curr.level]) {
const levelArr = acc[curr.level];
const last = levelArr[levelArr.length - 1];
if (last.length === 2) {
levelArr.push([curr])
} else {
last.push(curr)
}
} else {
acc[curr.level] = [
[curr]
];
}
return acc;
}, {})
};
function removeDuplicates(result) {
return Object.values(result.reduce((acc, curr) => {
acc[curr.player] = acc[curr.player] || curr;
return acc;
}, {}))
}
result = combine(removeDuplicates(data.data2));
var keys = Object.keys(result)
var html = ""
for (var i = 0; i < keys.length; i++) {
result[keys[i]].forEach(function(val) {
var length_ = val.length; //length of the json aray inside obj
val.forEach(function(value, index) {
var entryIDs = index == 0 ? "entryIDM[]" : "entryIDW[]"
var players = index == 0 ? "playerM[]" : "playerW[]"
var levels = index == 0 ? "levelM[]" : "levelW[]"
html += `<input type="text" name="${entryIDs}" value="${value.entryID}">
<input type="text" name="${players}" value="${value.player}">
<input type="text" name="${levels}" value="${value.level}">`
//if length is only one
if (length_ == 1) {
//just add inputs with nm..
html += `<input type="text" name="entryIDW[]" value="nm"> <input type="text" name="playerW[]" value="nm"><input type="text" name="levelW[]" value="nm">`
}
})
})
}
document.getElementById("result").innerHTML = html
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="result"></div>
This is my new codes and i can't append it when the length is = 1 or 2.
const source = [
{
entryID: 1,
entryName: "player1",
weight: 1900,
},
{
entryID: 2,
entryName: "player1",
weight: 1900,
},
{
entryID: 3,
entryName: "player3",
weight: 1905,
},
{
entryID: 4,
entryName: "player4",
weight: 1905,
},
{
entryID: 5,
entryName: "player5",
weight: 1910,
},
];
function combine(
data = [],
different = 0,
maxGroupSize = 2,
sortedStatement = (a, b) => a.weight - b.weight
) {
const sortedData = [...data].sort(sortedStatement);
const dataGroups = sortedData.reduce((acc, item) => {
const findedAccItem = acc.find(
(accItem) =>
accItem.length < maxGroupSize &&
accItem[0].weight + different >= item.weight &&
!accItem.find((obj) => obj.entryName === item.entryName )
);
if (findedAccItem) {
findedAccItem.push(item);
} else {
acc.push([item]);
}
return acc;
}, []);
const namedDataGroups = dataGroups.reduce((acc, item, index) => {
const key = [index, ...item.map((item) => item.weight)].join("_");
acc[key] = item;
return acc;
}, {});
return namedDataGroups;
}
console.log("Example #1: ", combine(source));
My target is to append the keys. Same as the first snippet. Any help will be appreciated. Thank you so much
You can get the length of the JSON Array outside result[keys[i]] each loop and then using that length you can change your code to append "no match" for length 1 .
Demo Code :
const source = [{
entryID: 1,
entryName: "player1",
weight: 1900,
},
{
entryID: 2,
entryName: "player2",
weight: 1900,
},
{
entryID: 3,
entryName: "player3",
weight: 1905,
},
{
entryID: 4,
entryName: "player4",
weight: 1905,
},
{
entryID: 5,
entryName: "player5",
weight: 1910,
},
];
function combine(
data = [],
different = 0,
maxGroupSize = 2,
sortedStatement = (a, b) => a.weight - b.weight
) {
const sortedData = [...data].sort(sortedStatement);
const dataGroups = sortedData.reduce((acc, item) => {
const findedAccItem = acc.find(
(accItem) =>
accItem.length < maxGroupSize &&
accItem[0].weight + different >= item.weight &&
!accItem.find((obj) => obj.entryName === item.entryName)
);
if (findedAccItem) {
findedAccItem.push(item);
} else {
acc.push([item]);
}
return acc;
}, []);
const namedDataGroups = dataGroups.reduce((acc, item, index) => {
const key = [index, ...item.map((item) => item.weight)].join("_");
acc[key] = item;
return acc;
}, {});
return namedDataGroups;
}
var result = combine(source);
var keys = Object.keys(result)
var html = ""
for (var i = 0; i < keys.length; i++) {
var length_ = result[keys[i]].length; //get length of the json array per keys
result[keys[i]].forEach(function(value, index) {
var entryIDs = index == 0 ? "entryIDM[]" : "entryIDW[]";
var players = index == 0 ? "playerM[]" : "playerW[]";
var levels = index == 0 ? "levelM[]" : "levelW[]";
//change to correct key name
html += `<input type="text" name="${entryIDs}" value="${value.entryID}">
<input type="text" name="${players}" value="${value.entryName}">
<input type="text" name="${levels}" value="${value.weight}">`
//if length is only one
if (length_ == 1) {
//just add inputs with nm..
html += `<input type="text" name="entryIDW[]" value="nm"> <input type="text" name="playerW[]" value="nm"><input type="text" name="levelW[]" value="nm">`
}
})
}
document.getElementById("result").innerHTML = html //append to dom
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="result"></div>
Related
I have the following array
{
agent_id:001,
priority:"High",
task_id:T1
},
{
agent_id:001,
priority:"High",
task_id:T1
},
{
agent_id:001,
priority:"Medium",
task_id:T1
}
{
agent_id:002,
priority:"High",
task_id:T1
}
I need to get an output of the above array as follows:
{agent_id:001,High_Count:2,Medium_Count:1},
{agent_id:002,High_Count:1,Medium_Count:0}
Here is my code:
for (let i = 0; i < dataModel.ticketList.length; i++) {
var priority = JSON.stringify(dataModel.ticketList[i].Priority);
if (!map.has(priority))
{
map.set(priority, {
Agent: dataModel.ticketList[i].Agent,
Low: 1,
});
}
else
{
map.get(priority).Low++;
}
}
const res = Array.from(map.values())
return res;
You could use Array.prototype.reduce like this:
const data = [
{ agent_id: 001, priority: "High", task_id: "T1" },
{ agent_id: 001, priority: "High", task_id: "T1" },
{ agent_id: 001, priority: "Medium", task_id: "T1" },
{ agent_id: 002, priority: "High", task_id: "T1" },
];
const result = data.reduce((acc, cur) => {
const index = acc.findIndex((x) => x.agent_id === cur.agent_id);
if (~index) {
const value = acc[index][`${cur.priority}_Count`] || 0;
acc.splice(index, 1, {
...acc[index],
[`${cur.priority}_Count`]: value + 1,
});
} else {
acc.push({ agent_id: cur.agent_id, [`${cur.priority}_Count`]: 1 });
}
return acc;
}, []);
console.log(result);
Please use Array.reduce() function.
const data = [{ agent_id:001, priority:"High", task_id:"T1" }, { agent_id:001, priority:"High", task_id:"T1" }, { agent_id:001, priority:"Medium", task_id:"T1" }, { agent_id:002, priority:"High", task_id:"T1" }];
const urgent = "Urgent";
let result = data.reduce((total, item) => {
if(total.map(val => val.agent_id).includes(item.agent_id)) {
const index = total.map(val => val.agent_id).indexOf(item.agent_id);
if(total[index].hasOwnProperty(item.priority + "_Count"))
total[index][item.priority + "_Count"] ++;
else
total[index][item.priority + "_Count"] = 1;
} else {
let obj = {agent_id: item.agent_id};
let priorities = data.map(val => val.priority).filter((val, i, self) => self.indexOf(val) === i);
if(!!urgent) priorities.push(urgent);
priorities.forEach(val => {obj[val + "_Count"] = 0});
obj[item.priority + "_Count"] = 1;
total.push(obj);
}
return total;
},[]);
result = result.map(val => {
const total = Object.keys(val).reduce((total, key) => key === 'agent_id' ? total : total + val[key], 0);
return {...val, total: total};
});
console.log(result);
I am making a matchmaking system where 2 players will be joined in 1 array if they have the same weight. My target is to combine my static code in to my dynamic code. The snippet I provide is my static code, it means that the data is not from DB. It works perfectly. However, when I'm combining my static code into my dynamic code, it is not working anymore. The second set of codes I provided below is a dynamic codes, it means that the data is based on what I'm fetching in DB. Any suggestions on how can I make it work? Been stuck for days. Any help will be appreciated. Thank you so much.
This is static code that works perfectly:
const source = [{
entryID: 1,
entryName: 'player1',
weight: 1,
},
{
entryID: 2,
entryName: 'player2',
weight: 1,
},
{
entryID: 3,
entryName: 'player3',
weight: 2,
},
{
entryID: 4,
entryName: 'player4',
weight: 2,
},
{
entryID: 5,
entryName: 'player5',
weight: 3,
},
{
entryID: 6,
entryName: 'player6',
weight: 3,
},
{
entryID: 7,
entryName: 'player7',
weight: 1,
},
{
entryID: 8,
entryName: 'player8',
weight: 1,
},
{
entryID: 9,
entryName: 'player9',
weight: 1,
},
{
entryID: 10,
entryName: 'player10',
weight: 1,
},
]
const combine = (source) => {
return source.reduce((acc, curr) => {
if (acc[curr.weight]) {
const levelArr = acc[curr.weight];
const last = levelArr[levelArr.length - 1];
if (last.length === 2) {
levelArr.push([curr])
} else {
last.push(curr)
}
} else {
acc[curr.weight] = [
[curr]
];
}
return acc;
}, {})
};
var result = combine(source)
var html = ""
var keys = Object.keys(result) //if there are more than one keys i.e : 2..
for (var i = 0; i < keys.length; i++) {
result[keys[i]].forEach(function(val) {
val.forEach(function(value, index) {
var entryIDs = index == 0 ? "entryIDM[]" : "entryIDW[]"
var handlers = index == 0 ? "handlerM[]" : "handlerW[]"
var weights = index == 0 ? "weightM[]" : "weightW[]"
html += `<input type="text" name="${entryIDs}" value="${value.entryID}">
<input type="text" name="${handlers}" value="${value.entryName}">
<input type="text" name="${weights}" value="${value.weight}">
`
})
})
}
document.getElementById("result").innerHTML = html //add html to div
console.log(result);
<div id="result">
</div>
This is my dynamic code (It is not working. My target output is the same as the output above). The data I'm fetching are based on Database.
ajax and script:
let ajaxResult = []; // the pushed data will be saved here
let save_method;
let table;
let base_url = "<?php echo base_url();?>";
let result = [];
var html = "";
// this is the part where I'm having a hard time to solve. My target is to combine my static code here in this dynamic part. When the condition is met, the result will be the same as above
const combine = (source) => {
return source[0].data.reduce((acc, curr) => {
const [entryID, eventID, entryName, weight] = curr;
if (acc[curr.weight]){
const levelArr = acc[curr.weight];
const last = levelArr[levelArr.length - 1];
if(last.length === 2) {
levelArr.push[{},{}]
} else {
last.push(curr)
}
}else{
acc[weight] = [{
eventID,
entryID,
entryName,
weight
}];
}
return acc;
}, {})
}
//end of script matching when 2 players have the same weight.
$(document).ready(function() {
//datatables
table = $("#table1").DataTable({
processing: false,
serverSide: true,
order: [],
searching: false,
paging: false,
info: false,
ajax: {
url: "<?php echo site_url('controller/fetch_players')?>",
type: "POST",
async: true,
dataType: "json",
success: function(data) {
ajaxResult.push(data); // I'm pushing my data to my ajaxResult variable
result = combine(ajaxResult); // Cleanup your data here.
console.log(combine(ajaxResult));
var keys = Object.keys(combine(ajaxResult)) //if there more then one keys i.e : 2..
for (var i = 0; i < keys.length; i++) {
console.log("Keys " + keys[i])
//loop through json array
result[keys[i]].forEach(function(val, index) {
//check if index value is `0`..change name.
var entryIDs = index == 0 ? "entryIDM[]" : "entryIDW[]"
var handlers = index == 0 ? "handlerM[]" : "handlerW[]"
var weights = index == 0 ? "weightM[]" : "weightW[]"
html += `<span> entryID </span><input type="text" name="${entryIDs}" value="${val.entryID}" readonly>
<span> Handler </span><input type="text" name="${handlers}" value="${val.entryName}" readonly>
<span> Weight </span><input type="text" name="${weights}" value="${val.weight}" readonly>
<span> eventID </span><input type="text" name="eventID[]" value="${val.eventID}" readonly>
<br>`
})
}
document.getElementById("result").innerHTML = html //add html to div
},
},
"columnDefs": [{
"targets": [0], //first column
"orderable": false, //set not orderable
},
{
"targets": [-1], //last column
"orderable": false, //set not orderable
},
],
});
});
Thank you to #swati
const combine = (source) => {
return source.reduce((acc, curr) => {
if (acc[curr.weight]) {
const levelArr = acc[curr.weight];
const last = levelArr[levelArr.length - 1];
if (last.length === 2) {
levelArr.push([curr])
} else {
last.push(curr)
}
} else {
acc[curr.weight] = [
[curr]
];
}
return acc;
}, {})
};
result = combine(data.data2);
console.log(result)
var keys = Object.keys(result)
for (var i = 0; i < keys.length; i++) {
result[keys[i]].forEach(function(val) {
val.forEach(function(value, index) {
Made some changes with my controller also on how i fetched my data.
I am working on an application where I need to get combine the object of same department based on the
conditions provided in the second Array and attach the relation to the object.
let inArr1 = [{"D1D2":"AND"},{"D3D4":"OR"}]
let inArr2 =[{"ID":"1","NAME":"KEN","DEPT1":"CSE"},
{"ID":"2","NAME":"MARK","DEPT2":"IT"},
{"ID":"3","NAME":"TOM","DEPT3":"ECE"},
{"ID":"4","NAME":"SHIV","DEPT4":"LIB"},
{"ID":"5","NAME":"TIM","DEPT5":"SEC"}
]
Output
outArr ={
[{"ID":"1","NAME":"KEN","DEPT1":"CSE","REL":"AND"},
{"ID":"2","NAME":"MARK","DEPT2":"IT","REL":"AND"}], //Arr1
[{"ID":"3","NAME":"TOM","DEPT3":"ECE","REL":"OR"},
{"ID":"4","NAME":"SHIV","DEPT4":"LIB","REL":"OR"}], //Arr2
[{"ID":"5","NAME":"TIM","DEPT5":"SEC"}] //Arr3
}
Code:
let condArr=[],outArr,i=1;
inArr1.forEach(condt => {
let dept = Object.keys(condt)[0];
let tmparr = dept.split("D");
tmparr.shift()
condArr.push(tmparr)
});
inArr2.forEach(condt => {
if(condArr.includes(inArr2.D+i)){
i++;
outArr.push(inArr2);
}
});
Your code has a bit confused logic, i would suggest rather this
let inArr1 = [{"D1D2":"AND"},{"D3D4":"OR"},{"D5D6":"AND"}]
let inArr2 =[{"ID":"1","NAME":"KEN","DEPT1":"CSE"},
{"ID":"2","NAME":"MARK","DEPT2":"IT"},
{"ID":"3","NAME":"TOM","DEPT3":"ECE"},
{"ID":"4","NAME":"SHIV","DEPT4":"LIB"},
{"ID":"5","NAME":"TIM","DEPT5":"SEC"},
{"ID":"6","NAME":"TLA","DEPT6":"SEC"},
]
// first lets create object of ids as keys and conditions as values
const [keys, conditions] = inArr1.reduce((agg, cond, index) => {
Object.entries(cond).forEach(([key, value]) => {
key.split('D').forEach(v => { if (v) agg[0][v] = { value, index }})
agg[1].push([])
})
return agg
}, [{}, []]) // {1: "AND", 2: "AND", 3: "OR", 4: "OR"}
conditions.push([])
// and now just map over all elements and add condition if we found id from the keys
inArr2.forEach(item => {
const cond = keys[item.ID]
if (cond) conditions[cond.index].push({...item, REL: cond.value})
else conditions[conditions.length - 1].push(item)
})
const res = conditions.filter(v => v.length)
console.log(res)
You could store the goups by using the ID and use new objects.
let inArr1 = [{ D1D2: "AND" }, { D3D4: "OR" }],
inArr2 = [{ ID: "1", NAME: "KEN", DEPT1: "CSE" }, { ID: "2", NAME: "MARK", DEPT2: "IT" }, { ID: "3", NAME: "TOM", DEPT3: "ECE" }, { ID: "4", NAME: "SHIV", DEPT4: "LIB" }, { ID: "5", NAME: "TIM", DEPT5: "SEC" }],
groups = inArr1.reduce((r, o) => {
Object.entries(o).forEach(([k, REL]) => {
var object = { REL, group: [] };
k.match(/[^D]+/g).forEach(id => r[id] = object);
});
return r;
}, {}),
grouped = inArr2.reduce((r, o) => {
var { REL, group } = groups[o.ID] || {};
if (group) {
if (!group.length) r.push(group);
group.push(Object.assign({}, o, { REL }));
} else {
r.push([o]);
}
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
can try other solution:
let inArr1 = [{ D1D2: "AND" }, { D3D4: "OR" }, { D6D7: "XOR" }];
let inArr2 = [
{ ID: "1", NAME: "KEN", DEPT1: "CSE" },
{ ID: "2", NAME: "MARK", DEPT2: "IT" },
{ ID: "3", NAME: "TOM", DEPT3: "ECE" },
{ ID: "4", NAME: "SHIV", DEPT4: "LIB" },
{ ID: "5", NAME: "TIM", DEPT5: "SEC" },
{ ID: "9", NAME: "BAR", DEPT5: "XYZ" },
{ ID: "6", NAME: "FOO", DEPT5: "XYZ" },
];
let unmatchedArr = []
let matchedArr = inArr2.reduce((acc, obj) => {
// getting index matched from inArr1 objects key
const indexMatched = getIndexMatch(obj.ID);
// creating index if not exists
if (!acc[indexMatched] && indexMatched !== null) acc[indexMatched] = [];
// if some index matched it merge current obj with DEL property with inArr1[indexMatched] key => value
return indexMatched !== null
? acc[indexMatched].push({
...obj,
DEL: inArr1[indexMatched][Object.keys(inArr1[indexMatched])[0]]
})
// pushing on unmatchedArr
: unmatchedArr.push(obj)
, acc
}, []);
function getIndexMatch(id) {
for (const [index, obj] of inArr1.entries()) {
for (const key of Object.keys(obj)) {
// spliting only digits of the current key of object
if (key.match(/\d/g).includes(id)) return index; // returning index of inArr1 if is included
}
}
return null;
}
// merging arrays
const result = [...matchedArr, unmatchedArr];
console.log(result);
Maybe this question has already been asked and answered somewhere but after searching for more than 3 hrs I'm asking this question.
Below is my JSON data
var my_data = [
{
"TempRture_qc": 4,
"VoltAGE": 44.09722,
"TempRture": 22.32,
"VoltAGE_qc": 55,
"_time": "2018-08-07T03:39:29.001Z"
},
{
"TempRture_qc": 2,
"VoltAGE": 42.09722,
"TempRture": 22.12,
"VoltAGE_qc": 0,
"_time": "2018-08-07T03:39:30.006Z"
},
{
"TempRture_qc": 1,
"VoltAGE": 43.09722,
"TempRture": 22.82,
"VoltAGE_qc": 0,
"_time": "2018-08-07T03:39:31.009Z"
}
];
desired output i need
[
{
"name": "TempRture_qc",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":4},
{"name":"2018-08-07T03:39:30.006Z","y":2},
{"name":"2018-08-07T03:39:33.017Z","y":1}
]
},
{
"name": "VoltAGE",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":44.09722},
{"name":"2018-08-07T03:39:30.006Z","y":42.09722},
{"name":"2018-08-07T03:39:33.017Z","y":43.09722}
]
},
{
"name": "TempRture",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":22.32},
{"name":"2018-08-07T03:39:30.006Z","y":22.12},
{"name":"2018-08-07T03:39:33.017Z","y":22.82}
]
},
{
"name": "VoltAGE_qc",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":55},
{"name":"2018-08-07T03:39:30.006Z","y":0},
{"name":"2018-08-07T03:39:33.017Z","y":0}
]
}
]
for getting this above output i have tried below code.
var accounting = [];
var fieldName = {};
for (var x in obj){
var mykey = Object.keys(obj[x]);
for (var mk in mykey){
if(mykey[mk]=='VoltAGE'){
fieldName.name = mykey[mk];
// accounting.push({
// "name":mykey[mk]
// })
}
if(mykey[mk]=='TempRture'){
fieldName.name = mykey[mk];
}
// console.log(mykey[mk]); //to get the key name
}
accounting.push({
"name" : obj[x]._time,
"y" : obj[x][employees.name],
})
fieldName.data = accounting;
}
console.log(fieldName );
by doing this what I'm getting is below JSON
{ name: 'TempRture',
data:
[ { name: '2018-08-07T03:39:29.001Z', y: 22.32 },
{ name: '2018-08-07T03:39:32.014Z', y: 22.12 },
{ name: '2018-08-07T03:39:33.017Z', y: 22.82 } ] }
I'm not able to understand how I will get the data in one JSON object.
For a solution with low time complexity, try .reduceing into an object indexed by keys of the inner object, creating a { name, data: [] } at that key in the accumulator if it doesn't exist there yet. Then, push to the data array, and get the values of the whole object:
var my_data=[{"TempRture_qc":4,"VoltAGE":44.09722,"TempRture":22.32,"VoltAGE_qc":55,"_time":"2018-08-07T03:39:29.001Z"},{"TempRture_qc":2,"VoltAGE":42.09722,"TempRture":22.12,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:30.006Z"},{"TempRture_qc":1,"VoltAGE":43.09722,"TempRture":22.82,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:31.009Z"}]
console.log(Object.values(
my_data.reduce((a, { _time, ...obj }) => {
Object.entries(obj).forEach(([name, val]) => {
if (!a[name]) a[name] = { name, data: [] };
a[name].data.push({ name: _time, y: val });
});
return a;
}, {})
));
var my_data=[{"TempRture_qc":4,"VoltAGE":44.09722,"TempRture":22.32,"VoltAGE_qc":55,"_time":"2018-08-07T03:39:29.001Z"},{"TempRture_qc":2,"VoltAGE":42.09722,"TempRture":22.12,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:30.006Z"},{"TempRture_qc":1,"VoltAGE":43.09722,"TempRture":22.82,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:31.009Z"}]
var keys = Object.keys(my_data[0])
var result= [];
for(i = 0; i<keys.length-1; i++) {
var obj = {name: keys[i],data: []}
obj.data = my_data.map(val=>({name: val["_time"], y: val[keys[i]]}));
result.push(obj);
}
console.log(result)
An understandable answer with map, findIndex and forEach functions will be
var my_data = [{ "TempRture_qc": 4, "VoltAGE": 44.09722, "TempRture": 22.32, "VoltAGE_qc": 55, "_time": "2018-08-07T03:39:29.001Z" }, { "TempRture_qc": 2, "VoltAGE": 42.09722, "TempRture": 22.12, "VoltAGE_qc": 0, "_time": "2018-08-07T03:39:30.006Z" }, { "TempRture_qc": 1, "VoltAGE": 43.09722, "TempRture": 22.82, "VoltAGE_qc": 0, "_time": "2018-08-07T03:39:31.009Z" } ],
result = [];
my_data.map(itm => {
let keys = Object.keys(itm);
keys.forEach(iitt => {
if (iitt != '_time') {
let index = result.findIndex(ii => {
return ii.name == iitt;
})
if (index == -1) {
result.push({
name: iitt,
data: []
});
result[result.length - 1].data.push({
name: itm["_time"],
y: itm[iitt]
})
} else {
result[index].data.push({
name: itm["_time"],
y: itm[iitt]
});
}
}
})
})
console.log(result)
I have a JSON array of products with nested array of stores that supply them:
let arrayOfProducts =
[
{
"title": "ProductA",
"stores": [
{
"name": "Store1",
"price": 15.09
},
{
"name": "Store2",
"price": 16.30,
},
{
"name": "Store4",
"price": 16.55,
},
.
.
.
"title": "ProductB",
"stores": [
{
"name": "Store1",
"price": 8.06
},
{
"name": "Store3",
"price": 9.25,
},
{
"name": "Store4",
"price": 9.27,
},
.
.
.
]
I need to find the combination of the minimum number of store(s)(due to extra shipping constraint) that provide all of the products at the lowest TOTAL price.
e.g. lets say the array has five products ProductA-ProductE.There is no single store in their respective arrays that can supply all of them. Store2 supplies a subset of the products and so does any other store.
The output should be like that:
[
{
"name": "store1",
"price": total_price_for_this_store,
"products": [ "ProductC"]
},
{
"name": "store2",
"price": total_price_for_this_store,
"products": [ "ProductA", "ProductB", "ProductD"]
},
{
"name": "store3",
"price": total_price_for_this_store,
"products": [ "ProductE"]
}
]
I have managed to create the expected output using javascipt's forEach and filter functions, but only to find the solution if one or more stores have ALL the products and not a subset of them.
let results = []
arrayOfProducts.forEach((product) => {
product.stores.forEach(store => {
let storeIndex = results.findIndex(el => { return el.name === store.name })
if (storeIndex === -1) { // first occurence of the shop in the array of results
results.push({
name: store.name,
price: store.price,
products : [product.title]
})
} else {
results[storeIndex].price += store.price
results[storeIndex].products.push(product.title)
}
})
})
let allProducts = results.filter((store) => {
return store.products.length === arrayOfProducts.length
})
allProducts.sort(function (a, b) {
return parseFloat(a.price) - parseFloat(b.price)
})
How can i approach this problem?I dont know how to start.
Does it belong to the LP category of algorithms?
I've managed to come up with a solution:
const fs = require('fs')
const _ = require('lodash')
// loading the product JSON file
let product = JSON.parse(fs.readFileSync('product.json', 'utf8'))
// create a sorted array of the title products
let productTitles = product.map(el => {
return el.title
}).sort(sortAlphabetically)
let numberOfProducts = productTitles.length
let storeCombinations = []
let stores = []
// create the array of stores
product.forEach((product) => {
product.stores.forEach(store => {
let price = store.price
let productUrl = store.productUrl
let storeIndex = stores.findIndex(el => { return el.name === store.name })
if (storeIndex === -1) { // first occurence of the shop in the array of results
stores.push({
name: store.name,
products: [{
title: product.title,
price: (parseFloat(price) * product.quantity).toFixed(2)
}]
})
} else {
stores[storeIndex].products.push({
title: product.title,
price: (parseFloat(price) * product.quantity).toFixed(2),
})
}
})
})
let comboCnter = 0
// for each of the stores see if the missing product(s) can be complemented
// with any of the following stores.
// If true then merge the two store products
stores.forEach((el, index) => {
for (let i = index + 1; i < stores.length; i++) {
let currentStoreProducts = el.products.map(product => product.title).sort(sortAlphabetically)
let nextStoreProducts = stores[i].products.map(product => product.title).sort(sortAlphabetically)
let mergedArrays = _.uniq(currentStoreProducts.concat(nextStoreProducts))
if (mergedArrays.length === numberOfProducts) {
let products1 = []
let products2 = []
let store1Price = 0
let store2Price = 0
productTitles.forEach(title => {
let index1 = el.products.findIndex(x => x.title === title)
let index2 = stores[i].products.findIndex(x => x.title === title)
if (index1 !== -1 && index2 !== -1) {
if (parseFloat(el.products[index1].price) < parseFloat(stores[i].products[index2].price)) {
store1Wins()
} else {
store2Wins()
}
}
if (index2 === -1) {
store1Wins()
}
if (index1 === -1) {
store2Wins()
}
function store1Wins() {
store1Price = (parseFloat(el.products[index1].price) + parseFloat(store1Price)).toFixed(2)
products1.push({
title: el.products[index1].title,
productUrl: el.products[index1].productUrl
})
}
function store2Wins() {
store2Price = (parseFloat(stores[i].products[index2].price) + parseFloat(store2Price)).toFixed(2)
products2.push({
title: stores[i].products[index2].title,
productUrl: stores[i].products[index2].productUrl
})
}
})
storeCombinations.push({
totalPrice: (parseFloat(store1Price) + parseFloat(store2Price)).toFixed(2),
store1: {
name: el.name,
price: store1Price,
products: products1
},
store2: {
name: stores[i].name,
price: store2Price,
products: products2
}
})
comboCnter++
}
}
})
// sort the final result ascending prices
storeCombinations.sort((a, b) => {
return parseFloat(a.totalPrice) - parseFloat(b.totalPrice)
})
fs.writeFileSync('storeCombinations.json', JSON.stringify(storeCombinations))
console.log('comboCnter: ' + comboCnter)
function sortAlphabetically(a, b) {
let nameA = a.toLowerCase()
let nameB = b.toLowerCase()
if (nameA < nameB) { return -1 }// sort string ascending
if (nameA > nameB) { return 1 }
return 0 // default return value (no sorting)
}
This code finds the cheapest combination of two stores.