I've got an ordered array of objects, myBros. I want each one to get its own index in the array (which I'm storing as myPlace) and store the ID of the following one, with the last object storing the ID of the first.
The code below results in each object storing the ID of the last object, not the next one. On a positive note, the last object stores the ID of the first one.
EDIT: Sorry, I should have been more specific-this function lives in an object (a React component) which has some props, one of which is an id. getNextBroId returns a value based on the component's index.
What am I doing wrong?
EDIT: reworked the code in accordance with Daniel Beck's suggestion, still having the same problem.
for (let i = 0; i < myBros.length - 1; i++) {
myBros[i].nextBroId = myBros[i + 1]._id;
}
myBros[myBros.length - 1].nextBroId = myBros[0]._id;
const myPlace = myBros.findIndex(p => p._id === id);
const getNextBroId = () => {
return myBros[myPlace].nextBroId;
};
EDIT: I've posted the entire component here: React-cycling through components in an array and am considering closing this question to avoid redundancy.
This is one of those cases where boring old iteration is going to be a lot easier (and more performant) than the more exciting new techniques.
var myBros = [
{_id: "a"},
{_id: "b"},
{_id: "c"},
{_id: "d"},
{_id: "e"}
]
// step through all but last element in the array, link each one to the next
for (var i = 0; i < myBros.length - 1; i++) {
myBros[i].nextBroId = myBros[i + 1]._id;
}
// link the last one back to the first
myBros[myBros.length - 1].nextBroId = myBros[0]._id;
// and we're done
console.log(myBros);
(I'm not entirely sure why you would want to turn an array into a pseudo linked list, but I assume you have your reasons...)
Related
I have an array of users who all need to be added to a group array. If the the group array has less than 3 users, i want to add the user to that group array. If the group array already has 3 user, I want to push the current group array to another array that collects all the groups and start another new group array for the next 3 users until there are no users.
Error -
let group[i] = [];
Unexpected token [
I have been racking my brains trying to figure this out. Maybe staring at the screen for too long.
This is what i have been trying with different variations but the console is not impressed -
function createGroups(totalPeople){
let i = 1
let group[i] = [];
let array = totalPeople
totalPeople.map((user) => {
if(group[i] =< 3){
group[i].push(user)
}else{
array.push(group[i]);
i++
}
})
};
totalPeople is an array created earlier in my code and this is the only part of the file that is not running as intended. Any help with a method on how to do this or suggestions on fixing this code would be of great help! thank you!
Try to initialize group as an array:
let i = 1
let group = [] // Initialize as an array
group[i] = [];
let array = totalPeople
totalPeople.map((user) => {
if(group[i] =< 3){
group[i].push(user)
}else{
array.push(group[i]);
i++
}
})
There are a few issues in your code :
function createGroups(totalPeople){
let i = 1
let group[i] = []; // issue #1
let array = totalPeople
totalPeople.map((user) => {
if(group[i] =< 3){ // issues #2 and #3
group[i].push(user)
}else{
array.push(group[i]); // issue #4
i++; // issue #5
}
})
};
Issue #1 :
You need to define group as an array before adding an index.
let group = [];
group[i] = [];
Issue #2 :
Looks like you meant to compare group[i].length and 3
Issue #3 :
Use <= instead of =< to compare your numbers. Also, if you compare the length with <= 3, you'll have 4 people per group. Because the first index in arrays is 0.
Issue #4 :
You are pushing to array, which is a reference to totalPeople. Is this what you meant? Because I doubt it will produce the expected results. You may want to initialize an empty array and push your group[i] array in it. And then, return that new array. It's usually a good practice in functionnal programming to return a new array and not modify the one passed as a parameter.
Issue #5 :
If you increment i, you need to initialize group[i] as an array, otherwise you won't be able to push in it when comes the next loop iteration.
Differnet logic :
Now that you fixed the issues in your code, here's a Snippet showing another way to do it using Array.prototype.reduce :
const totalPeople = ["Joe", "Jack", "Jerry", "Jane", "Mary", "Billy", "Vicky", "Bobby"];
const groupsOfThree = totalPeople.reduce((accumulator, currentPerson, index) => {
// pushing the current person in the topest group in the accumulator
accumulator[accumulator.length-1].push(currentPerson);
// if it's the 3rd person, we're pushing the an empty group in the accumulator
if (index % 3 === 2) {
accumulator.push([]);
}
return accumulator;
}, [[]]); // the initial value of the accumulator will be an array containing an empty group
console.log(groupsOfThree);
I have a list that I took from a converted CHANGELOG.md file, and it looks like this:
["[3.0.0]","Features", "changes done in file","[2.0.1]", "Bug Fixes", "fixed login"]
What I want to do is to separate each version into its own list, like this:
["[3.0.0]", "Features", "changes done in file"],
["[2.0.1]", "Bug Fixes", "fixed login"]
Obviously, because it's a changelog, there can be multiple features and multiple bugfixes in a single version, so I want to a piece of code that separates the code appropriately.
I tried using if (string.startsWith('[')) but i couldn't manage to fit it in a loop.
Any help is appreciated.
Here's something I came up with. The code basically loops through the input array and adds each string to a currentArray variable. Everytime it hits a [ it puts the currentArray into the output and clears currentArray. At the end it removes the first element as the first element of the output will always be an empty array (since the first element of the input starts with a [)
var input = ["[3.0.0]","Features", "changes done in file","[2.0.1]", "Bug Fixes", "fixed login"];
var output = [];
var currentArray = [];
for (var i = 0; i < input.length; i++) {
if (input[i].charAt(0) == '[') {
output.push(currentArray);
currentArray = [];
}
currentArray.push(input[i]);
}
output.push(currentArray);
currentArray = [];
//Since it will take the first one, and put empty one, need to do last step.
output.splice(0, 1);
console.log(output);
// ["[3.0.0]", "Features", "changes done in file"],
// ["[2.0.1]", "Bug Fixes", "fixed login"]
Assuming that you're always working in sets of three, this is a quick and ugly approach
var data = ["[3.0.0]","Features", "changes done in file","[2.0.1]", "Bug Fixes", "fixed login"],
items = [];
data.map( (el, idx) => {
var last = items.length;
if( idx % 3 === 0 ) {
items.push( [] );
last += 1;
}
last = items[ last - 1 ];
last.push( el );
} );
console.log( JSON.stringify( items ) );
Here's an alternative solution should you prefer it:
const arr = ["[3.0.0]","Features", "changes done in file","[2.0.1]", "Bug Fixes", "fixed login"];
const newArr = [];
let tempArr = [];
arr.forEach(function(v, i) {
if(/^\[\d+.\d+.\d\]$/.test(v) && i > 0) {
newArr.push(tempArr);
tempArr = [v];
} else {
tempArr.push(v)
}
});
newArr.push(tempArr);
console.log(newArr);
This snippet loops through the items one-by-one. It uses two arrays, one to hold the final result and one to populate with items for the current version.
I am using a regex to check if the item contains one [ followed by a number, then a period, number, period, number and finally the trailing ]. This allows the other strings that are not version tags to contain that character.
If the current item is a version tag, we push tempArr (which contains the changes of the current version that we've previously filled in our loop) to our result array newArr. Then, we empty the tempArr and give it the starting value of the next version tag.
If it is not, we just push the current item to our temporary array.
It would be interesting to know if you were guaranteed to get this data in triplets, as your example seems to imply. If you knew this up front, there are many creative solutions that could emerge. For just creating a 2D Array, however, I like this approach (you can run this directly in node.js to try it out):
const original = ['[3.0.0]', 'Features', 'changes done in file', '[2.0.1]', 'Bug Fixes', 'fixed login']
function transformToChangeLog (originalArray) {
const changeLog = originalArray.reduce((newList, element) => {
element.charAt(0) === '[' // check for version string
? newList.push([element]) // If version string, then push a new Array containing that string
: newList[newList.length - 1].push(element) // If something else, tack it onto the last Array in the changelog list
return newList // whatever is returned in the reduce function is passed to the next iteration, allowing us to build this 2D array one element at a time.
}, [])
return changeLog
}
console.log(transformToChangeLog(original))
I hope that helps! I like the reduce Array method, because of it's versatility and succinctness.
Hello i have selectbox to show from javascript array.
<h6><strong>Brand</strong></h6>
<select data-placeholder="Brand..." multiple id="brand" style="width:200px;height:50px;margin: 0;">
<script language="javascript">print_brand1("brand");</script>
</select>
But when i choose one of selectbox data then it get values. Here is javascript code
var brand_arr = new Array(
1:"Acer",
2:"Dell"
);
function print_brand1(brand_id){
var option_str = document.getElementById(brand_id);
option_str.length=0;
option_str.selectedIndex = 0;
for (var i=0; i<brand_arr.length; i++) {
option_str.options[option_str.length] = new Option(brand_arr[i],brand_arr[i]);
}
}
How do i get keys (not values) when i choose one of selectbox data?
Your code will not work in its current state, however if you insist on keeping your current array structure you would need:
var brand_arr = {
1: "Acer",
2: "Dell"
};
Which syntactically makes little sense, assigning a key of 1/2 makes little sense when you could just get this number by using the current index + 1.
Note that with the changes above, in order to assign the keys 1 and 2 we are having to iterate over a Javascript Object and not a Javascript Array.
If you wanted to print 1 and 2 you could then do:
for(var k in brand_arr) {
if(brand_arr.hasOwnProperty(k)) {
console.log(k); // key
console.log(brand_arr[k]); // value
}
}
Although this answers your problem, I would still consider the structure of your data, it would make much more sense to have something like:
var brand_arr = [
{ brand: 'Acer', price: 200 },
{ brand: 'Dell', price: 200 }
];
Note how in the above example we can add extra details about that brand in the JSON. We could then iterate over the brand array to get more detail about each brand in a much more readable format opposed to a user assigned index and its name.
I'm working with a large dataset that needs to be efficient with its Mongo queries. The application uses the Ford-Fulkerson algorithm to calculate recommendations and runs in polynomial time, so efficiency is extremely important. The syntax is ES6, but everything is basically the same.
This is an approximation of the data I'm working with. An array of items and one item being matched up against the other items:
let items = ["pen", "marker", "crayon", "pencil"];
let match = "sharpie";
Eventually, we will iterate over match and increase the weight of the pairing by 1. So, after going through the function, my ideal data looks like this:
{
sharpie: {
pen: 1,
marker: 1,
crayon: 1,
pencil: 1
}
}
To further elaborate, the value next to each key is the weight of that relationship, which is to say, the number of times those items have been paired together. What I would like to have happen is something like this:
// For each in the items array, check to see if the pairing already
// exists. If it does, increment. If it does not, create it.
_.each(items, function(item, i) {
Database.upsert({ match: { $exist: true }}, { match: { $inc: { item: 1 } } });
})
The problem, of course, is that Mongo does not allow bracket notation, nor does it allow for variable names as keys (match). The other problem, as I've learned, is that Mongo also has problems with deeply nested $inc operators ('The dollar ($) prefixed field \'$inc\' in \'3LhmpJMe9Es6r5HLs.$inc\' is not valid for storage.' }).
Is there anything I can do to make this in as few queries as possible? I'm open to suggestions.
EDIT
I attempted to create objects to pass into the Mongo query:
_.each(items, function(item, i) {
let selector = {};
selector[match] = {};
selector[match][item] = {};
let modifier = {};
modifier[match] = {};
modifier[match]["$inc"] = {};
modifier[match]["$inc"][item] = 1
Database.upsert(selector, modifier);
Unfortunately, it still doesn't work. The $inc breaks the query and it won't let me go more than 1 level deep to change anything.
Solution
This is the function I ended up implementing. It works like a charm! Thanks Matt.
_.each(items, function(item, i) {
let incMod = {$inc:{}};
let matchMod = {$inc:{}};
matchMod.$inc[match] = 1;
incMod.$inc[item] = 1;
Database.upsert({node: item}, matchMod);
Database.upsert({node: match}, incMod);
});
I think the trouble comes from your ER model. a sharpie isn't a standalone entity, a sharpie is an item. The relationship between 1 item and other items is such that 1 item has many items (1:M recursive) and each item-pairing has a weight.
Fully normalized, you'd have an items table & a weights table. The items table would have the items. The weights table would have something like item1, item2, weight (in doing so, you can have asymmetrical weighting, e.g. sharpie:pencil = 1, pencil:sharpie = .5, which is useful when calculating pushback in the FFA, but I don't think that applies in your case.
Great, now let's mongotize it.
When we say 1 item has many items, that "many" is probably not going to exceed a few thousand (think 16MB document cap). That means it's actually 1-to-few, which means we can nest the data, either using subdocs or fields.
So, let's check out that schema!
doc =
{
_id: "sharpie",
crayon: 1,
pencil: 1
}
What do we see? sharpie isn't a key, it's a value. This makes everything easy. We leave the items as fields. The reason we don't use an array of objects is because this is faster & cleaner (no need to iterate over the array to find the matching _id).
var match = "sharpie";
var items = ["pen", "marker", "crayon", "pencil"];
var incMod = {$inc:{}};
var matchMod = {$inc:{}};
matchMod.$inc[match] = 1;
for (var i = 0; i < items.length; i++) {
Collection.upsert({_id: items[i]}, matchMod);
incMod.$inc[items[i]] = 1;
}
Collection.upsert({_id: match}, incMod);
That's the easy part. The hard part is figuring out why you want to use an FFA for a suggestion engine :-P.
I'm building a web-app that needs to process nested geographical data to both display in a treeview, but also be searchable. The raw data looks something like this:
id:1, name:UK
id:2: name: South-East, parentId: 1
id:3: name: South-West, parentId:1
id:4: name: Berkshire, parentId: 2
id:5: name: Reading, parentId: 4
and I want it to look something like this:
id:1: name UK, children[
{id: 2, name: South-East, children:[
{id:4: name: Berkshire, children: [
{id:5: name: Reading}
]
},
{id:3: name: South-West}
]
so that each geographical location has a "children" array property, which contains all the sub-areas, each of which has another "children" array property, and so on. It would probably make sense to have a "parent" property as well, so I could navigate from any child item up to its parent.
I also need to be able to search the list - searching each branch of the tree may take some time, so perhaps I need to also keep the list in flat format.
I know how I could do this in JavaScript (possibly using jLinq for filtering, grouping and sorting), but I don't know how fast it would be. Has anyone already had a go at this in JavaScript or know of any general algorithms/patterns that solve this?
It's actually not that difficult to make the flat array into a tree and do it pretty quickly, I think the slowest bit will be getting the definition of the data structure onto the page (hence why you're lazy loading was successful!). This can be helped though by making the data structure definition smaller.
In Javascript I did it like this:
//Make the data definition as small as possible..
//each entry is [ name, parent_pos_in_array]..
//note: requires that a parent node appears before a child node..
var data = [
["UK", -1], //root..
["South-East", 0],
["South-West", 0],
["Berkshire", 1],
["Reading", 3]
//...etc...
];
//Turns given flat arr into a tree and returns root..
//(Assumes that no child is declared before parent)
function makeTree(arr){
//Array with all the children elements set correctly..
var treeArr = new Array(arr.length);
for(var i = 0, len = arr.length; i < len; i++){
var arrI = arr[i];
var newNode = treeArr[i] = {
name: arrI[0],
children: []
};
var parentI = arrI[1];
if(parentI > -1){ //i.e. not the root..
treeArr[parentI].children.push(newNode);
}
}
return treeArr[0]; //return the root..
}
var root = makeTree(data);
To test the speed on a larger list you can run:
var data = [['root', -1]];
for(var i = 1; i < 100000; i++){
var parentI = Math.floor(Math.random()*(i-1));
data.push(['a_name', parentI]);
}
var start = new Date().getTime();
var tree = makeTree(data);
var end = new Date().getTime();
console.log('Took: ' + (end-start) + 'ms.');
With 100000 elements in the array it takes < 200ms on my old desktop. Not sure what kind of performance is acceptable though!
If you have a simple id & parent-id objects array with no other clue on the level, it's a tough task to generate the nested form. I would assume recursive approaches won't be practical in long lists. The best method that i could come up with so far is sorting that array in such a way that all children come after their parents. Parents and children and even the root objects can be mixed but a child must come after it's parent. Assuming that the object structure is like var data = [{id: KL442, pid: HN296}, {id: ST113, pid: HN296}, {id: HN296, pid: "root"},...] Yet sorting is the first phase of the job. While sorting we can generate a LUT (look up table) to access the parents almost at no cost. At the exit of the outer loop just one single instruction lut[a[i].id]=i; is sufficient for this. This makes the job enormously fast at the nesting stage. This is the sorting and LUT preparation phase.
function sort(a){
var len = a.length,
fix = -1;
for (var i = 0; i < len; i++ ){
while(!!~(fix = a.findIndex(e => a[i].pid == e.id)) && fix > i) [a[i],a[fix]] = [a[fix],a[i]];
lut[a[i].id]=i;
}
return a;
}
Once you have it sorted than a reverse iteration is the only thing you have to do to get your nested structure. So that you now have your data array sorted and LUT prepared, then this is the code for nesting.
for (var i = sorted.length-1; i>=0; i--)
sorted[i].pid != "root" && (!! sorted[lut[sorted[i].pid]].children
&& sorted[lut[sorted[i].pid]].children.push(sorted.splice(i,1)[0])
|| (sorted[lut[sorted[i].pid]].children = [sorted.splice(i,1)[0]]));
For a working sample you can check a previous answer of mine.
With Lodash:
var index = _.mapKeys(data,'id');
var obj = {};
_.each(index,function(v){
if(!v.parentId){
obj[v.id]=v;
}else{
if(!index[v.parentId].children){
index[v.parentId].children=[];
}
index[v.parentId].children.push(v);
}
});
Demo is here