I am searching for an algorithm to reorder an list of dictionarys or in javascript array of objects.
For example I have the following list of objects:
my_dict = [
{
"id": 123,
"priority": 1
},
{
"id": 234,
"priority": 2
},
{
"id": 345,
"priority": 3
},
{
"id": 654,
"priority": 4
}
]
Now I want to change the item with id 654 to be the first priority. So the other items gets automatically to priority 2,3 and 4.
This should be the result:
my_dict = [
{
"id": 654,
"priority": 1
}
{
"id": 123,
"priority": 2
},
{
"id": 234,
"priority": 3
},
{
"id": 345,
"priority": 4
},
]
Another example is when I want to decrease the priority of item 234 to priority 3 then the item with priority 3 should become priority 2 (from the original/first dict). There are should no priority be twice in this list and there should not be gaps.
my_dict = [
{
"id": 123,
"priority": 1
},
{
"id": 345,
"priority": 2
},
{
"id": 234,
"priority": 3
},
{
"id": 654,
"priority": 4
}
]
For clarification here is an other example.
If I move item with id 123 to priority 4 the item before should become priority 1,2 and 3.
my_dict = [
{
"id": 234,
"priority": 1
},
{
"id": 345,
"priority": 2
},
{
"id": 654,
"priority": 3
}
{
"id": 123,
"priority": 4
},
]
The list should always start with priority 1. Can somebody explain how I can implement it with python or javascript? I tried it with a for loop starting and 1 but this does not work.
The discussions about a better data structure are spot-on. But if you still want to implement what you asked for, we can write a straightforward JS version that does the naive thing:
const changePriority = (xs, id, to,
idx = xs .findIndex (({id: i}) => i == id),
ys = [...xs .slice (0, idx), ...xs .slice (idx + 1)]
) =>
[ ...ys . slice (0, to - 1), xs [idx], ...ys .slice(to - 1)]
.map ((x, i) => ({...x, priority: i + 1}))
const dict = [{id: 123, priority: 1}, {id: 234, priority: 2}, {id: 345, priority: 3}, {id: 654, priority: 4}]
console .log (
changePriority (dict, 654, 2)
)
.as-console-wrapper {max-height: 100% !important; top: 0}
Note that this depends upon your id and the new priorty, to actually existing in the input. It wouldn't be hard to add error-checking.
Also note that this returns a new array, and doesn't mutate the original; we're not barbarians here.
I disagree with the idea of having priority be represented by both the position in the list and the "priority" member. This is redundant and will cause trouble at some point.
Further, I have to make assumptions to answer this question, as the question is not complete.
Assumptions:
In the end, the list should be sorted by priority again
Priorities should end up being integers
Priorities always start at 1 and enumerate the list without gaps
First approach
Specify exactly where you want to put your item by using halfs. This is simple and everything else should fall in place.
Example
Task: Move 234 to priority 3.
Sadly, it is not uniquely defined what this is supposed to mean, as it is unclear where the old item of priority 3 should move to.
Instead move it to priority 3.5 to make it unambiguous, then re-sort and re-enumerate:
# Change priority of '234' to '3.5'
my_dict[1]["priority"] = 3.5
# Sort
my_dict.sort(key=lambda x: x["priority"])
# Re-enumerate
for (position, item) in enumerate(my_dict):
item["priority"] = position + 1
print(my_dict)
[{'id': 123, 'priority': 1}, {'id': 345, 'priority': 2}, {'id': 234, 'priority': 3}, {'id': 654, 'priority': 4}]
For large datasets this is really slow though, as every single priority change is O(n*log(n)), as it requires a sort.
You should investigate in better data structures, like a heap or a linked list, depending on your exact usage scenario.
Second approach
If we remove the 'priority' member, we can write a much more efficient algorithm:
def move(data, original, target):
data.insert(target, data.pop(original))
# Important: Start priorities at 0, not 1. This makes this a lot easier.
my_dict = [123, 234, 345, 654]
move(my_dict, 3, 0)
print(my_dict)
my_dict = [123, 234, 345, 654]
move(my_dict, 1, 2)
print(my_dict)
[654, 123, 234, 345]
[123, 345, 234, 654]
https://ideone.com/o9UcCb
For a longer discussion about how to move an item inside of a list, read this discussion.
Related
Still learning here. This one as simple as it seems, has beaten me up. I have managed to get the answer. But, when I do, I am getting more than one name coming out of my if statement. I used a new set to remove the duplicate names in the names array. But frankly, that seems lazy to me and I feel something can be done that is better.
Can someone show me some ways I'm missing to better go through this problem? I tried flattening and that didn't work either. Thank you for your help!
Here are the directions given to me:
Create a function passingStudents that accepts an array of student objects.
It should iterate through the list of students and return an array of the names of all the students who have an average grade of at least 70.
function passingStudents(students) {
const names = [];
students.forEach(student => {
student.grades.forEach(grade => {
if(grade.score >= 70) {
names.push(student.name);
}
});
});
let uniqueChars = [...new Set(names)];
return uniqueChars;
}
//Uncomment the lines below to test your function:
var students = [
{
"name": "Marco",
"id": 12345,
"grades": [{"id": 0, "score": 65}, {"id": 1, "score": 75}, {"id": 2, "score": 85}]
},
{
"name": "Donna",
"id": 55555,
"grades": [{"id": 0, "score": 100}, {"id": 1, "score": 100}, {"id": 2, "score": 100}]
},
{
"name": "Jukay",
"id": 94110,
"grades": [{"id": 0, "score": 65}, {"id": 1, "score": 60}, {"id": 2, "score": 65}]
}
];
console.log(passingStudents(students)); // => [ 'Marco', 'Donna' ]
The reason your solution introduces multiple people in the names array is that it adds the name for each grade they have that is at least 70. This means it will return a student if any of their grades is at least 70 rather than if their average grade is at least 70. While your solution may pass the test case provided, it will not work for every case in general. A better solution is to average the grades by using reduce and filter the original list of students based on whether their average is above 70. Then you can map the filtered students to their name.
function passingStudents(students) {
return students
.filter((student) => {
const { grades } = student;
const average = grades.reduce((sum, grade) => sum + grade.score, 0) / grades.length;
return average >= 70;
})
.map((student) => student.name);
}
I am attempting to push one index of my array into it's own array. I am attempting this by using the forEach() method.
I have an array that is nested inside of an array revData that has multiple indexes inside of its array. I expecting to push out only index 5 of the array into its own array so I can graph the data.
At the moment using doing my forEach method, my newArr only has the first index 5 five times.
My expected out come is to have the newArr have 3 results coming from the values of index 5 from revData such as :
newArr = [ 24343.1 , 44321.0, 43242.8 ]
Here is an example of my code :
let revData = [
[1, 1, 1, "22", "online stores", 24343.1 ],
[2, 2 ,2, "13", "retail stores", 44321.0],
[ 3, 3, 3, "7", "walk ins", 43242.8]
]
const newArr = []
revData[0].forEach(function () {
newArr.push(revData[0][5])
})
console.log(newArr)
Probably it should be like this
let revData = [
[1, 1, 1, "22", "online stores", 24343.1],
[2, 2, 2, "13", "retail stores", 44321.0],
[3, 3, 3, "7", "walk ins", 43242.8],
];
const newArr = [];
revData.forEach(function (child) {
newArr.push(child[5]);
});
console.log(newArr);
Or you can use map instead
const newArr = revData.map((child) => child[5]);
console.log(newArr);
You are iterating over revData[0] so you won't get the other items. Also, during the iteration you always read the value in revData[0]. You should use the first parameter of the forEach callback, like this:
let revData = [
[1, 1, 1, "22", "online stores", 24343.1],
[2, 2, 2, "13", "retail stores", 44321.0],
[3, 3, 3, "7", "walk ins", 43242.8]
]
const newArr = []
revData.forEach(function(item) {
newArr.push(item[5])
})
console.log(newArr)
One available approach here is to use a for... of loop.
Everyone will have their own preferred approach, but I generally find that, for syntactic clarity, I favour for...of loops over forEach.
Working Example:
let revData = [
[1, 1, 1, "22", "online stores", 24343.1 ],
[2, 2 ,2, "13", "retail stores", 44321.0],
[3, 3, 3, "7", "walk ins", 43242.8]
];
const newArr = [];
for (arrayElement of revData) {
newArr.push(arrayElement[5]);
}
console.log(newArr);
The language here is a little off and I'll do my best to answer.
When you say "push out" I am assuming you want to remove the element out of the array. The correct term for removing an element from the array is "pop". "Pop" is generally related to removing an element an array, while "push" is generally related to adding an element to an array.
Also Do not name your newArr as a const. const is meant to indicate values that never change. By naming your newArr as a const, it cannot change.
Now to answer your question, I can see what you are trying to do, but your syntax is a little wrong.
let revData = [
[1, 1, 1, "22", "online stores", 24343.1 ],
[2, 2 ,2, "13", "retail stores", 44321.0],
[ 3, 3, 3, "7", "walk ins", 43242.8]
]
let newArr = []
revData.forEach(function(item) {
newArr.push(item[5])
})
console.log(newArr)
https://codepen.io/junghole/pen/BajdZPq?editors=0012
I suggest that you gain a better understanding of 2d arrays and for each loops. A good resource is geeks for geeks, or simply googling. Good Luck!
I'm practicing how to maniupulate data in JS in this article: http://learnjsdata.com/combine_data.html
var articles = [
{"id": 1, "name": "vacuum cleaner", "weight": 9.9, "price": 89.9, "brand_id": 2},
{"id": 2, "name": "washing machine", "weight": 540, "price": 230, "brand_id": 1},
{"id": 3, "name": "hair dryer", "weight": 1.2, "price": 24.99, "brand_id": 2},
{"id": 4, "name": "super fast laptop", "weight": 400, "price": 899.9, "brand_id": 3}
];
var brands = [
{"id": 1, "name": "SuperKitchen"},
{"id": 2, "name": "HomeSweetHome"}
];
articles.forEach(function(article) {
var result = brands.filter(function(brand){
return brand.id === article.brand_id;
});
delete article.brand_id;
article.brand = (result[0] !== undefined) ? result[0].name : null;
});
I'm confused with the last part: article.brand = (result[0] !== undefined) ? result[0].name : null;
I understand the conditional operation: it wants to have null value if result[0] is not defined. But I'm wondering what result[0] refers to. I thought it would take first object: {"id":2, "name": "HomeSweetHome"} so there should be for loop to iterate all objects in order to see if objects meet the condition? Could you inform me what I'm missing or/and what result[0] refers to?
Thanks,
result[0] will be undefined in case there is no element in result. result is expected to be an array of brands filtered by the filter operation
The filtered array result will have same brand as that of the current article in the outer foreach loop. The filter condition is going to achieve that.
It looks like in this particular case you will get only one element in result array always as there are unique brand ids. It might have more elements in case of duplicated brand ids.
result[0] points to first element in the array result
So I have an array of object that I want to use for my server, each object has the same amount of keys.
Here is a simplified version of the array:
[{
"BTQ": 1,
"COLLIC": "Foo1",
"MMC": "Bar1",
"PTURE": 39,
"STOCK": 6,
},
{
"BTQ": 1,
"COLLIC": "Foo1",
"MMC": "Bar2"",
"PTURE": 31,
"STOCK": 2,
},
{
"BTQ": 2,
"COLLIC": "Something1",
"MMC": "AnotherThing1",
"PTURE": 40,
"STOCK": 5,
},
{
"BTQ": 2,
"COLLIC": "Something1",
"MMC": "AnotherThing1",
"PTURE": 11,
"STOCK": 7,
}]
and I want to build a function that loops through all of these objects, and builds another JSON array that looks like that:
[
{
"BTQ": 1,
"COLLICs": {
"Foo1": {
"Bar1": {
"Stock": 6,
"PTURE" {"39": 6}
},
"Bar2": {
"Stock": 2,
"PTURE": {"31": 2,}
}
}
}
},
{
"BTQ": 2,
"COLLICs": {
"Something1": {
"AnotherThing1": {
"Stock": 12,
"PTURE": {"40": 5, "11": 7}
}
}
}
},
//etc ...
]
I am using NodeJS, and I figured out I needed to build a function like this one :
function step(i){
if (i < raw.length){
data.push(raw[i].A);
step(i + 1);
} else {
console.log("Finished");
console.log(data);
}
}
but the thing is I don't know how to nest this function...
Here is what I want to achieve in the end:
1) Loop through each object in the raw array (done with the function above)
2) For each object, loop through data (the sorted array) and see if any of the objects in there has the same "BTQ" value, if so : go into the object, and see if the raw[i].COLLIC exists the data[j].COOLLICs' keys. If exists, look if the MMC exists, ETC ... If any of the values does not exist, create a new key/object which stores all the informations, and then start again with the following raw object.
It is probably not very clear and I'm sorry for that, if it can help, I had built the same algorithm but with for loops, I can paste it here if it could help to make things clearer.
I really hope you can help me because I'm loosing my mind on this ...
Thanks ! Ali
This is a continuation of exploration traversing data structures in JavaScript.
See here and here.
Given this Javascript object:
var parsed = {
"terms": [
{
"span": [
12,
13
],
"value": "2",
"label": "number"
},
{
"span": [
13,
14
],
"value": "x",
"label": "multiply"
},
{
"span": [
14,
16
],
"value": "14",
"label": "number"
},
],
"span": [
12,
21
],
"weight": 0.85,
"value": "2x14 test"
};
How would I derive an array of the indexes of the terms where the label: number?
In a previous question, noted above, I was able to solve the notion of deriving the index for a certain label, when it was known that there was only one instance of such.
parsed.terms.map(function(d){ return d['label']; }).indexOf('number');
Now I am faced with the notion of multiple instances, as in the object above. The above code snip will only derive the index of the first.
I could build an array by looping through the terms and see if each has a number label, however the ideal solution would perhaps expand or modify the code snip above and perhaps not use a loop.
When you need to map and filter simultaneously use reduce:
var indexes = parsed.terms.reduce(function(indexCollection, item, index) {
if(item.label === 'number') {
indexCollection.push(index);
}
return indexCollection;
}, []);