This question already has answers here:
JavaScript "new Array(n)" and "Array.prototype.map" weirdness
(14 answers)
Closed 4 years ago.
I have an array of objects that looks like this:
const input = [
{id: 3, value: 2},
{id: 0, value: 3},
{id: 2, value: 8},
{id: 1, value: 5},
{id: 0, value: 2},
{id: 1, value: 6}
]
And I am trying to build an array of the maximum value by id, with id as index. For our example the desired output is the following:
const output = [ 3, 6, 8, 2 ]
I am also assuming that I know the number of unique ids ahead of time, and that they start at 0 and rise sequentially. My first whack at this was to .map() over the an empty array of the right length and build intermediate arrays with .filter() for each id, then use Math.max() on the filtered arrays.
const myAttempt = Array(4).map((_, index) => {
const filtered = input.filter(item => item.id === index);
return Math.max(filtered);
});
All I get out of this is:
myAttempt = [ 4 empty slots ];
I suspect I'm totally off-base with the Array(4) bit, and that the answer might involve .reduce(), but I never really got the hang of reduce so any help would be very much appreciated.
PS: I'd prefer answers that avoid the use of libraries in the vein of lodash or jQuery.
Use Array.reduce() to collect the values highest value of each key. Convert to array using Array.values():
const input = [
{id: 3, value: 2},
{id: 0, value: 3},
{id: 2, value: 8},
{id: 1, value: 5},
{id: 0, value: 2},
{id: 1, value: 6}
]
const result = Object.values(input.reduce((r, { id, value }) => {
r[id] = +r[id] > value ? r[id] : value;
return r;
}, {}));
console.log(result);
If all ids from 0 on wards appear in the array, you can add the values by their id (index) to an array accumulator:
const input = [
{id: 3, value: 2},
{id: 0, value: 3},
{id: 2, value: 8},
{id: 1, value: 5},
{id: 0, value: 2},
{id: 1, value: 6}
]
const result = input.reduce((r, { id, value }) => {
r[id] = +r[id] > value ? r[id] : value;
return r;
}, []);
console.log(result);
Related
I feel this is a basic problem but I am having a hard time coming up with a more elegant solution.
So my problem is that I have an array object with a field for Priority.
The priority would be sequential from 1 being the highest.
For example I have:
[{id: 1, priority: 1}, {id: 2, priority: 2}, {id: 3, priority: 3}]
Then I want to change the priority of id: 3 into 1 which the resulting array would be
[{id: 1, priority: 2}, {id: 2, priority: 3}, {id: 3, priority: 1}]
Right now my solution is looping the lenght of array starting with the Priority value that's going to be change and adding + 1. A lot of complication comes up with this approach and I feel I'm making it to complicated on what it should be
An easy, if not necessarily completely efficient, solution would be to temporarily allow non-integer priorities, sort the list by priority, then renumber the priorities.
E.g. if you have
[
{ id: "a", priority: 1 },
{ id: "b", priority: 2 },
{ id: "c", priority: 3 },
]
and you'd need to add a d with a priority between b and c, you could temporarily assign it (2 + 3) / 2, i.e. 2.5:
[
{ id: "a", priority: 1 },
{ id: "b", priority: 2 },
{ id: "c", priority: 3 },
{ id: "d", priority: 2.5 }
]
Then sort the list by priority (using whichever method you like), so you get
[
{ id: "a", priority: 1 },
{ id: "b", priority: 2 },
{ id: "d", priority: 2.5 }
{ id: "c", priority: 3 },
]
and then renumber the priorities with
itemsInPriorityOrder.forEach((item, index) => {
item.priority = index + 1;
});
to end up with
[
{ id: "a", priority: 1 },
{ id: "b", priority: 2 },
{ id: "d", priority: 3 },
{ id: "c", priority: 4 }
]
This will of course also work for any other modification of priority.
You could exploit array indices to reorder your priorities! The below code moves things around so that the items are in an array (sorted by priority, but without an actual priority field), then just moves around the items in the list and restores the field:
const setPriority = (items, id, priority) => {
const _items = [...items].sort(byPriority)
const index = _items.findIndex(x => x.id === id)
const stripped = stripPriorities(_items)
moveItem(stripped, index, priority - 1)
return restorePriorities(stripped)
}
const moveItem = (arr, oldIndex, newIndex) => {
const temp = arr.splice(oldIndex, 1)[0]
arr.splice(newIndex, 0, temp)
}
const stripPriorities = arr =>
arr.map(({ priority, ...x }) => x)
const restorePriorities = arr =>
arr.map((x, i) => ({ ...x, priority: i + 1 }))
const byPriority = (x, y) =>
x.priority > y.priority
Here's a working demo: https://jsfiddle.net/kadft7ep/3/
Increase Priority by 1
Your objects
let data = [{id: 1, priority: 1}, {id: 2, priority: 2}, {id: 3, priority: 3}]
Object to whome you want to increase priority by 1
lets say id=2
first find the object, i am assuming you found it in let obj.
now
obj.priority-=1.5
// resulting data will be [{id: 1, priority: 1}, {id: 2, priority: 0.5}, {id: 3, priority: 3}]
after sorting data would look something like
[{id: 2, priority: 0.5}, {id: 1, priority: 1}, {id: 3, priority: 3}]
now the same approach to make them as whole numbers
data = data.map((obj, index) => {
obj.priority = index + 1;
});
// result [{id: 2, priority: 1}, {id: 1, priority: 2}, {id: 3, priority: 3}]
Make Most Prioritize
Instead of -1.5 just make priority 0 & then sort & then whole numbers again
I am having the array of objects like below
Array 1:
[{id: 1, name: 'Golden', isEdited: true}, {id: 2, name: 'Pearl'}]
Array 2:
[{id: 1, name: 'Golden'}, {id: 2, name: 'Pearlblue'}, , {id: 3, name: 'Orange'}]
Now i would like to merge the two arrays if the object contains isEdited flag means then that object should not be updated.
Expected result should be
[{id: 1, name: 'Golden', isEdited: true}, {id: 2, name: 'Pearlblue'}, {id: 3, name: 'Orange'}]
I have tried with the below approach
b.map((battr) => {
return {
...a[battr.id],
...battr
}
})
But it returns the output as
[{id: 1, name: 'Golden'}, {id: 2, name: 'Pearlblue'}, {id: 3, name: 'Orange'}]
Be carefull, your ids don't match your array indexes.
Hence in your attempt a[battr.id] should be replaced by something like a.find(a => a.id === battr.id)
Your example is confusing since in both arrays the name is 'Golden' for id=1, but assuming you want only the properties from array A if isEdited=true, your solution could be :
b.map((b_attr) => {
var matchingA = a.find(a_attr => a_attr.id === b_attr.id);
if (matchingA && matchingA.isEdited)
return matchingA;
else
return { ...matchingA, ...b_attr }
})
I have an example array:
arr = [{id:1, count: 2}, {id: 2, count: 6}, {id: 2, count: 4}, {id: 1, count:4}]
I need transform it to include arrays with objects inside based on id:
[[{id:1, count: 2}, {id: 1, count:4}], [{id: 2, count: 6}, {id: 2, count: 4}]]
If I will have 3 different ids - then it will have 3 arrays inside and so on.
If you know any good solutions - let me know. Lodash could be ok as well.
You can use groupBy from lodash to get a map like this:
{
'1': [{id:1, count: 2}, {id: 1, count:4}],
'2': [{id: 2, count: 6}, {id: 2, count: 4}]]
}
Then you can transform it to an array using Object.values()
Essentially you need these two lines:
const groupedById = _.groupBy(items, item => item.id);
const result = Object.values(groupedById);
Pure JS, with reduce:
arr.reduce((acc, curr) => {
let existing = acc.findIndex(elem => elem.some(obj => obj.id === curr.id));
if (existing > -1) {
acc[existing].push(curr);
}
else {
acc[acc.length] = [curr]
}
return acc;
}, []);
As you mentioned in your question lodash solution could also work for you, then loadash has one out of the box method groupBy which can achieve your desired result.
import { groupBy } from "lodash";
const arr = [{id:1, count: 2}, {id: 2, count: 6}, {id: 2, count: 4}, {id: 1, count:4}]
const result = groupBy(arr, 'id');
console.log(result)
Working DEMO
#domenikk showed a really good example! Also, you could use Ramda instead of Lodash to have a point-free function =)
const arr = [
{id:1, count: 2},
{id: 2, count: 6},
{id: 2, count: 4},
{id: 1, count:4}
]
const groupById = R.compose(
R.values,
R.groupBy(R.prop('id'))
)
console.log(groupById(arr))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
This question already has answers here:
Using array map to filter results with if conditional
(5 answers)
Map and filter an array at the same time
(16 answers)
Closed 3 years ago.
I have an object as shown below:
var obj = [
{id: 1, name: 'AD', key: 10},
{id: 2, name: 'AD', key: 20},
{id: 3, name: 'BD', key: 30},
{id: 4, name: 'CD', key: 40}
];
I want to filter and create a new array which satisfies any condition. For eg: filter which have name as 'AD' and create a new array of key as:
[10, 20]
Tried .map
obj.map(ele => {
return ele.name === 'AD' ? ele.key : null;
}); //it adds even nulls in the result array as [10, 20, null, null]
Tried .filter:
obj.filter(ele => {
return ele.name === 'AD' ? ele.key : null;
});
Result: [{id: 1, name: "AD", key: 10}, {id: 2, name: "AD", key: 20}] //gives array of objects, not what I'm expecting.
Thanks in advance
First filter the array and then map to get the value:
obj.filter(e => e.name === 'AD').map(e => e.key)
Another option is flatmap (check browser compatibility here)
obj.flatMap(e => e.name === 'AD' ? [e.key] : [])
First of all obj is an array in your code.
Now the solution is simple you filter first and then map like this:
obj.filter(item => item.name === 'AD').map(item => item.key);
You can use reduce():
var obj = [{id: 1, name: 'AD', key: 10},{id: 2, name: 'AD', key: 20},{id: 3, name: 'BD', key: 30},{id: 4, name: 'CD', key: 40}];
var result = obj.reduce((acc, cur) => {
if (cur.name == "AD") acc.push(cur.key)
return acc;
}, []);
console.log(result);
var obj = [
{id: 1, name: 'AD', key: 10},
{id: 2, name: 'AD', key: 20},
{id: 3, name: 'BD', key: 30},
{id: 4, name: 'CD', key: 40}
];
function specialFilter(filt) {
return obj.filter(el => el.name === filt).map(el => el.key)
}
console.log(specialFilter("AD"))
I have the following Javascript object:
var icecreams = [{
name: 'vanilla',
price: 10,
rating: 3
}, {
name: 'chocolate',
price: 4,
rating: 8
}, {
name: 'banana',
price: 1,
rating: 1
}, {
name: 'greentea',
price: 5,
rating: 7
}, {
name: 'moosetracks',
price: 6,
rating: 2
}, ];
I need to access the "related properties" (not sure of the exact terminology) in each section of the object. For example, if I am given the name "vanilla," I need to access a "price" of 10 and "rating" of 3. Is there a way to do this without changing the object's structure? Possibly using this?
You can use Array.prototype.filter()
The filter() method creates a new array with all elements that pass the test implemented by the provided function.
var vanilla = icecreams.filter(function(o){
return o.name === 'vanilla'
});
//As filter return's an array you need to use index thus [0] to access first element of array
console.log(vanilla[0].price);
console.log(vanilla[0].rating);
In ECMAScript 2015 you can find object in your array like this:
var vanilla = icecreams.find(o => o.name === 'vanilla')
console.log(vanilla.price);
console.log(vanilla.rating);
ES5 analogy:
var vanilla = icecreams.reduce(function(r, o) {
return r || (o.name === 'vanilla' ? o : undefined);
}, undefined);
console.log(vanilla.price);
console.log(vanilla.rating);
var icecreams = [ {name: 'vanilla', price: 10, rating: 3}, {name: 'chocolate', price: 4, rating: 8}, {name: 'banana', price: 1, rating: 1}, {name: 'greentea', price: 5, rating: 7}, {name: 'moosetracks', price: 6, rating: 2}, ];
var found = {};
for(i in icecreams){
if(icecreams[i].name == 'vanilla'){
found = icecreams[i];
break;
}
}
console.log('Price: ' + found.price);
console.log('Price: ' + found.rating);
if you have control over the structure of the variable icecream, I'd uses an oject instead of an array to hold the flavors. This makes it simple to access the values by name.
var icecream = {
vanilla: {price: 10, taring: 3},
banana: {price: 1, taring: 1},
greentea: {price: 5, taring: 7},
moosetracks: {price: 6, taring: 2}
};
Or I'd probably adapt them if they came via AJAX (or another dynamic source):
//Assume icecream follows your original structure.
function tranformFlavors(flavors) {
var flavorObj = {};
flavors.forEach(item){
flavorObj[item.name] = item;
delete flavorObj[item.name].name;
}
return flavorObj;
}
icecream = transformFlavors(icecream);
If you're using ES5, you can use underscore or lodash for this.
http://underscorejs.org/#find
or
http://underscorejs.org/#filter
or
http://underscorejs.org/#where
or
http://underscorejs.org/#findWhere