I have a function that takes two lists(each item in the two lists are the same type). It only adds item from the second list to the first list if the item in the second list does not exist in the first list. To determine if it exist in the list, I compare the property pk.
addUniqueItemsToList: function (sourceList, toAddList) {
for (var a = 0; a < toAddList.length; a++) {
var doesItemExist = false;
for (var b = 0; b < sourceList.length; b++) {
if (sourceList[b].pk == toAddList[a].pk) {
doesItemExist = true;
break;
}
}
if (!doesItemExist) {
sourceList.push(toAddList[a]);
}
}
}
Is there a way in javascript where instead of comparing pk, I can compare it to other properties of the object, by passing in the name of the property to the function? i.e., addUniqueItemsToList: function (sourceList, toAddList, propertyName)
Yes you can compare by object property directly and access properties dinamically using string as key ej array['mykey']. Also it would be better if instead of doing a for inside a for (1for - n for) create a map in order to avoid so much iterations:
Eg: Number iterations without a map when items.length = 100 & anotherItems.length = 200
100*200 = 20000 possibles iterations.
Eg. Number of iterations creating a map with items.length = 100 & anotherItems.length = 200
300 iterations.
Example of how i do it:
var items = [{_id: 1, text: "Text 1"}, {_id:2, text: "Text 2"}];
var anotherItems = [{_id: 1, text: "Text 1"}];
var mapByProperty = function(array, prop) {
var map = [];
for (var i = 0, len = array.length; i !== len; i++) {
map[array[i][prop]] = array[i];
}
return map;
};
var commonUniqueProperty = '_id';
var mappedAnotherItemsById = mapByProperty(anotherItems, commonUniqueProperty);
for(var i = 0, len = items.length; i !== len; i++) {
if(mappedAnotherItemsById[items[i][commonUniqueProperty]]) {
console.log(items[i]);
}
}
Related
I've read a few answers on similar questions but they didn't seem to match my problem exactly. I have an array of objects. I'm trying to create a duplicate of an item(s) and insert it at a certain position, but it doesn't work and when I am looking at the array in the debugger I'm seeing that the very first item of the element now has priKey = 0.
I'm suspecting that I'm running into the problem of the referencing the same item and not making a true clone and that's why I'm ending up with the first element priKey = 0.
Before inserting new Items I have 26 items in the elementsToInsert:
{priKey: 170, internlPo: 7, department: "RETAIL ", category: "WINTERGEAR", item: "BOOTS-SALO"}
{priKey: 171, internlPo: 7, department: "RETAIL ", category: "WINTERGEAR", item: "BOOTS-SALO"}
{priKey: 172, internlPo: 7, department: "RETAIL ", category: "WINTERGEAR", item: "BOOTS-SALO"}
My objects have more properties, I'm showing just a few.
Here is my current code and I'm trying to figure out where exactly the problem lies:
/**
* Copy line item (either all matrix items for this itemId or just that matrix item)
* */
purchaseOrdersCrudController.prototype.copyLineItem = function (lineItem, index, copyAll) {
const self = this;
let elementsToInsert = [];
_.forEach(self.model.lineItems, function (value, key) {
if (value.itemId === lineItem.itemId) {
if (copyAll || key === index) {
elementsToInsert.push(value); // new elements to insert
}
}
});
let i, startPos;
let len = elementsToInsert.length;
startPos = index + len;
for (i = 0; i < len; i++) {
elementsToInsert[i].priKey = 0;
elementsToInsert[i].qtyRcvd = 0;
self.model.lineItems.splice(startPos + i, 0, elementsToInsert[i]); // Insert new element
}
// Re-number
let rn = 0;
len = self.model.lineItems.length;
for (i = 0; i < len; i++) {
if (self.model.lineItems[i].itemId === lineItem.itemId) {
rn++;
if (self.model.lineItems[i].inventId === 0) {
self.model.lineItems[key].rowNumber = 1;
}
else {
self.model.lineItems[key].rowNumber = rn; // Make sure the row number is in sequence
}
}
};
this.form.$setDirty();
}
The idea of that code is to duplicate an item. If in my interface I'm clicking on the "header" row, I want to copy all items where itemId equals the selected item itemId and insert them all after the last item for that particular itemId (I may have several). If I click on a "regular" row, I just want to duplicate that line and insert it right below my current line and re-number the remaining items for that itemId. I'm struggling to make that code to work and not seeing where is my mistake.
I got it to work, as I suspected the problem was in cloning the object properly. This is my current code which does what I need (it seems in my quick test). I'd appreciate suggestions to improve it:
purchaseOrdersCrudController.prototype.copyLineItem = function (lineItem, index, copyAll) {
const self = this;
let elementsToInsert = [];
let newElement = {};
_.forEach(self.model.lineItems, function (value, key) {
if (value.itemId === lineItem.itemId) {
if (copyAll || key === index) {
newElement = Object.assign({}, value);
newElement.priKey = 0;
newElement.qtyRcvd = 0;
elementsToInsert.push(newElement); // new elements to insert
}
}
});
let i, startPos;
let len = elementsToInsert.length;
startPos = index + len;
for (i = 0; i < len; i++) {
self.model.lineItems.splice(startPos + i, 0, elementsToInsert[i]); // Insert new element
}
// Re-number
let rn = 0;
len = self.model.lineItems.length;
for (i = 0; i < len; i++) {
if (self.model.lineItems[i].itemId === lineItem.itemId) {
rn++;
if (self.model.lineItems[i].inventId === 0) {
self.model.lineItems[i].rowNumber = 1;
}
else {
self.model.lineItems[i].rowNumber = rn; // Make sure the row number is in sequence
}
}
};
this.form.$setDirty();
}
Would be really grateful for any help. Im pretty new to javascript and cant figure out how to solve this problem that I am having. Basically I have 2 arrays. One contains an object with an id value and a corresponding group value. The second array contains only the id. I would like to compare the ids of both the arrays and if they match I would like to extract the corresponding group value.
E.g.
a = [1,2,3,4,5];
b = [{1:group1},{2:group2},{3:group3}];
If id in a matches id in b then print out the id's group value
var a = [];
var b = [];
var c = {};
if (condition) {
c = {id:group}
b.push(c)
}
if (condition) {
a.push(id)
}
for (var i = 0; i < a.length; i++) {
//If id value in a exists in b, get id's corresponding group value from b
}
function find() {
for (var i = 0; i < a.length; i++) {
for (var j = 0; j < b.length; j++) {
if (b[j].hasOwnProperty(a[i])) {
return b[j][a[i]];
}
}
}
}
An alternative solution:
<script>
a = [
1, // index 0
2, // index 1
3, // index 2
4, // index 3
5 // index 4
];
b = [
{1:'group1'}, // index [0][1]
{2:'group2'}, // index [1][2]
{3:'group3'} // index [2][3]
];
// If id in a matches id in b then print out the id's group value
var i = 1;
for (var key in b) {
var bKeys = Object.keys(b[key]);
if(bKeys[0] == a[key]) {
console.log(b[key][i]);
}
i++;
}
</script>
What I'm working on is a menu that auto updates its entries based on an array length. It adds groups of 10 objects' properties (in this case "IDnumbers") to the menu if a new object is added to the array.
var arraysOfObject = [], obj = {"IDNumber": ""};
for(i = 0; i<42; i++){
arraysOfObject.push({"IDNumber": "Number " + i});}
Above is the array holding 42 objects with a specific property.
var array2 = [];
var leftOver = arraysOfObject.length % 10;
var groupsOfTen = (arraysOfObject.length - leftOver)/10;
for (var i = 0; i < groupsOfTen; i++) {
array2.push([]);
for (var j = i*10; j < i*10 + 10; j++)
array2[i].push(arraysOfObject[j]["IDNumber"]);
}
//now the leftover
if (leftOver > 0) {
array2.push([]);
for (var i = groupsOfTen*10; i < arraysOfObject.length; i++)
array2[array2.length-1].push(arraysOfObject[i]["IDNumber"]);
}
The array2 above is the array that stores all the possible arrays that can be grouped by 10 from arraysOfObject. In this case there are 5 inside of it, because 4 arrays holds 40 objects, and 1 array holds the 2 remainders.
That all works fine, but placing the array2 inside the menu displays all possible IDnumbers grouped together, but not grouped individually. I have to declare each possible array inside of it like so sets = [array2[0], array2[1], array2[2], array2[3], array2[4]]; If there's a 6th possible array because object #51 has been added to arraysOfObject, I have to input it with array2[5].
I don't want it to depend on my input, but that it knows the number of possible arrays and that it displays it automatically in sets. How do I do that?
var gui = new dat.GUI();
var guiData = function() {
this.message = "Dat.Gui menu";
this.system = 0;
this.Sets = 0;
};
var data = new guiData();
sets = [array2[0], array2[1], array2[2], array2[3], array2[4], array2[5]];
gui.add(data, 'message', 'Dat.Gui Menu!');
gui.add(data, 'system', {
"1": 0,
"2": 1,
"3": 2,
"4": 3,
"5": 4,
"6": 5,
}).name('system #').onChange(function(value) {
updateSets(value);
});
gui.add(data, 'Sets', sets[0]).onChange();
function updateSets(id) {
var controller = gui.__controllers[2];
controller.remove();
gui.add(data, 'Sets', sets[id]).onChange();
data.Sets = 0;
gui.__controllers[2].updateDisplay();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.1/dat.gui.min.js"></script>
<script>
var arraysOfObject = [], obj = {"IDNumber": ""};
for(i = 0; i<42; i++){
arraysOfObject.push({"IDNumber": "Number " + i});}
var array2 = [];
var leftOver = arraysOfObject.length % 10;
var groupsOfTen = (arraysOfObject.length - leftOver)/10;
for (var i = 0; i < groupsOfTen; i++) {
array2.push([]);
for (var j = i*10; j < i*10 + 10; j++)
array2[i].push(arraysOfObject[j]["IDNumber"]);
}
//now take care of the leftover
if (leftOver > 0) {
array2.push([]);
for (var i = groupsOfTen*10; i < arraysOfObject.length; i++)
array2[array2.length-1].push(arraysOfObject[i]["IDNumber"]);
}
</script>
Not the issue at hand, but I was playing around with the dat.gui as you posted it and was wondering if the dropdown could be refilled without removing/adding/etc. It seems to work with .options. (NB The initialization code makes heavy use of ES6, but can work without. The system menu is created dynamically from the sets array)
let arraysOfObject =Array.from({length:42}, (o,i) => "Number " + i),
ch =10, sets = Array.from({length:Math.ceil(arraysOfObject.length/ch)}, (a,i) => arraysOfObject.slice(i*=ch, i+ch));
var gui = new dat.GUI();
var guiData = function() {
this.message = "Dat.Gui menu";
this.system = 0;
this.Sets = 0;
};
var data = new guiData();
gui.add(data, 'message', 'Dat.Gui Menu!');
gui.add(data, 'system', sets.reduce((obj,s,i) => (obj[i+1] = i, obj), {})).name('system #').onChange(updateSets);
let controller = gui.add(data, 'Sets');
updateSets(0);
function updateSets(id) {
controller = controller.options(sets[data.Sets = id]);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.1/dat.gui.min.js"></script>
I think the easiest solution would be to use ES2015's spread operator which I don't know if you would want to use yet...
ES2015 method (demo)
sets = [...array2];
There are a few other changes in the demo to set the system variable
But after taking a closer look, you can optimize the code by using the method from this SO answer to chunk your array using slice(). Also, I'm not sure why an object was used to create array entries when it just ends up as a string... demo
var arraysOfObject = [],
system = {},
chunk = 10,
size = 92;
for (var i = 0; i < size; i++) {
arraysOfObject.push("Number " + i);
}
var sets = [];
var index = 0;
for (i = 0; i < size; i += chunk) {
sets.push(arraysOfObject.slice(i, i + chunk));
system[index + 1] = index++;
}
var gui = new dat.GUI();
var guiData = function() {
this.message = "Dat.Gui menu";
this.system = 0;
this.Sets = 0;
};
var data = new guiData();
gui.add(data, 'message', 'Dat.Gui Menu!');
gui
.add(data, 'system', system)
.name('system #')
.onChange(function(value) {
updateSets(value);
});
gui.add(data, 'Sets', sets[0]).onChange();
function updateSets(id) {
var controller = gui.__controllers[2];
controller.remove();
gui.add(data, 'Sets', sets[id]).onChange();
data.Sets = 0;
gui.__controllers[2].updateDisplay();
}
I want to write a function that takes an array such as:
var columns = ['distance', 'times', 'acceleration']
Then from this array, I want to generate something like this:
[{id: id_0, distance: 0, times: 0, acceleration: 0}, {id: id_1, distance: 1, times: 1, acceleration: 1}]
Notice that we have 2 objects here, but I want it to be whatever number I pass in to my parameter. Here is what I have:
generateData: function(rows, columns) {
var generatedData = [];
for (var i = 0, rowLen = rows.length; i < rowLen; i++) {
for (var n = 0; i < columns.length; n++) {
// not sure how to construct an object here from looping through my columns array
generatedData.push({
id: 'id_ + n',
// confused here
});
}
return generatedData;
}
}
This is the perfect place to dynamically create your own function. Try this:
function createArrayOfObjects(columns, count) {
var objectProps = new Array(columns.length);
for (var i = 0; i < columns.length; i++){
//":j" will be the variable j inside the dynamic function
objectProps[i] = columns[i] + ":j";
}
var funcBody = "var arr = new Array(count);" +
"for(var j = 0; j < count; j++){" +
"arr[j] = {" + objectProps.join(',') + "};" +
"}" +
"return arr;";
//Create a new function and call it with count as the parameter, returning the results
return new Function("count", funcBody)(count);
}
var count = 10;
var columns = ['distance', 'times', 'acceleration'];
createArrayOfObjects(columns.concat('id'), count);
This has the benefit of only having to loop over the columns array once where other solutions require nested loops.
JSPerf
I am giving you away the initial non-optimized solution. Its upto you to do the optimizations.
generateData: function(rows, columns) {
var generatedData = [];
for (var i = 0; i < rows.length; i++) {
var myObj = {};
myObj["id_" + i] = i;
for (var n = 0; n < columns.length; n++) {
myObj[columns[n]] = i;
}
generatedData.push(myObj);
}
return generatedData;
}
A functional approach that will take the object properties from the passed in array, instead of hard-coding them, might look something like this inside the for loop to populate an array named 'rows' with property names coming from the values of an array named 'cols':
cols.forEach(function(cv, ci, ca) { rows[ri][cv] = ri; });
See the snippet for a full example. Note that, in this example, I'm just shoving the current index of "rows" into the object as the property value.
var columns = ['distance', 'times', 'acceleration'];
function generateData(numRows, cols) {
rows = new Array(numRows);
for(ri=0; ri < rows.length; ri++) {
rows[ri] = { id: ri };
cols.forEach(function(cv, ci, ca) {
rows[ri][cv] = ri;
});
}
return rows;
}
data = generateData(5, columns);
console.log(data);
I have a string like this:
var str = 'My_Type_1=SSD&My_Value_1=16GB&My_Category_1=Disk Capacity&My_Type_2=Sony
&My_Value_2=PS4&My_Category_2=Console&My_rowOrder=2,1';
The string mostly has 3 parts except the last key:
Part 1 -> My - is a Common Prefix
Part 2 -> Type or Value or Category and it can keep changing
Part 3 -> It's a numeric value binding Part 1, Part 2 and Part 3 like Spreadsheet row.
The last key is always called
My_rowOrder and it's a comma delimeted value. It specifies how to construct the output array.
In the above example, 2,1 means a key value pair of
My_Type_2=Sony&My_Value_2=PS4&My_Category_2=Console should be the first in the output array.
Using JavaScript, I would like to parse the string and create an array out of it, such that the output is:
Array
(
[ 0 ] => Array
(
[Type] => Sony
[Value] => PS4
[Category] => Console
[Row] => 2
)
[ 1 ] => Array
(
[Type] => SSD
[Value] => 16GB
[Category] => Disk Capacity
[Row] => 1
)
)
How can I do this? I am partially able to do it this way:
function StringToArray(string) {
var request = {};
var pairs = string.split('&');
for (var i = 0; i < pairs.length-1; i++) {
var pair = pairs[i].split('=');
request[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
}
//I think I am in the right track, but need assistance
}
Your example output uses associative arrays, which JavaScript doesn't have, but you can use an array of objects instead.
This example outputs an array of objects, in the order specified by the rowOrder parameter. It trims the prefix (defined by prefix), and also trims the row number from the end of the key.
This will also work with the parameters in any order - e.g. you can mix them and it will parse as necessary, and the rowOrder parameter can appear anywhere in the string (doesn't have to be at the end).
Demo
function StringToArray(string) {
var prefix = 'My_'; // set the prefix
var output = [], request = [];
var pairs = string.split('&');
var order;
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
if (pair[0].replace(prefix, '') == 'rowOrder') {
order = pair[1];
} else {
var key = decodeURIComponent(pair[0]);
var pos = key.lastIndexOf('_');
var trimmedKey = key.substring(0, pos).replace(prefix, '');
var row = key.substring(pos + 1);
var value = decodeURIComponent(pair[1]);
var found = false;
for (var j = 0; j < output.length; j++) {
if (output[j].Row == row) {
output[j][trimmedKey] = value;
found = true;
}
}
if (!found) {
var obj = { 'Row': row };
obj[trimmedKey] = value;
output.push(obj);
}
}
}
// do the ordering based on the rowOrder parameter
var orderList = order.split(",");
for(var k=0; k<orderList.length; k++){
for(var l=0; l<output.length; l++){
if(output[l].Row == orderList[k]){
request.push(output[l]);
break;
}
}
}
return request;
}
Outputs an array of objects in the order specified by the My_rowOrder parameter:
[
{
Row: "2",
Type: "Sony",
Value: "PS4",
Category: "Console"
},
{
Row: "1",
Type: "SSD",
Value: "16GB",
Category: "Disk Capacity"
}
]
This may works for you...
<script>
var data = "My_Type_2=Sony&My_Value_2=PS4&My_Category_2=Console";
var array = new Array();
alert(JSON.stringify(URLToArray(data)));
function URLToArray(url) {
var request = {};
var pairs = url.substring(url.indexOf('?') + 1).split('&');
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
request[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
}
return request;
}
</script>
Try this:
function StringToArray(string) {
var request = [[],[]];
var pairs = string.split('&');
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
request[pair[0].slice(-1)-1][decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
}
//console.log(request)
}