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
Related
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:
Javascript - sort array based on another array
(26 answers)
Closed 3 years ago.
I have an array that looks like this:
order = [ "foo", "bar", "baz" ];
This array contains the attribute values I would like to sort my array of objects by. I want to sort the data so that all the objects with name "foo" are first, then "bar", then "baz". My array of objects looks something like this:
data = [
{name: "foo", score: 8},
{name: "baz", score: 4},
{name: "baz", score: 9},
{name: "foo", score: 6},
{name: "bar", score: 9}
];
I want to outcome of the data order to look like this, the array is ordered by name but also by score desc when the names are the same:
sortedData = [
{name: "foo", score: 8},
{name: "foo", score: 6},
{name: "bar", score: 9},
{name: "baz", score: 9},
{name: "baz", score: 4}
];
Here is the code I have tried so far:
order.forEach(name => {
sortedData = [...this.data].sort(function(obj1, obj2) {
return (
-(obj1.name) || obj2.score < obj1.score
);
});
});
console.log(sortedData);
You can simply use sort and indexOf
let order = [ "foo", "bar", "baz" ];
let data = [
{name: "foo", score: 8},
{name: "baz", score: 4},
{name: "baz", score: 9},
{name: "foo", score: 6},
{name: "bar", score: 9}
];
let op = data.sort((a,b)=> (order.indexOf(a.name) - order.indexOf(b.name)) || b.score - a.score )
console.log(op)
Use the index to sort based on the order, to get index you can use Array#indexOf method. To sort based on the number just return the difference.
// extract object properties for comparing
// return difference of indexes to sort based on that
// in case indexes are same return difference of score(to sort element with same name)
data.sort(({ name: a, score: as }, { name: b ,score: bs}) => order.indexOf(a) - order.indexOf(b) || bs - as)
let order = ["foo", "bar", "baz"];
let data = [{
name: "foo",
score: 8
},
{
name: "baz",
score: 4
},
{
name: "baz",
score: 9
},
{
name: "foo",
score: 6
},
{
name: "bar",
score: 9
}
];
data.sort(({ name: a, score : as }, { name: b ,score:bs}) => order.indexOf(a) - order.indexOf(b) || bs - as)
console.log(data)
We can use the array indices in the order array for sorting.
Also using object destructuring assignment to get the object keys for comparison.
If the first comparison of name key results in equality of the two keys then going for the second criteria of the score key.
In my answer I did not mutate the original array, if that kind of behavior is needed you can check this solution:
const order = [ "foo", "bar", "baz" ];
const data = [
{name: "foo", score: 8},
{name: "baz", score: 4},
{name: "baz", score: 9},
{name: "foo", score: 6},
{name: "bar", score: 9}
];
function sortArray(data, order){
const sortedArr = Array.from(data).sort(({name: name1, score: score1},{name: name2, score: score2}) =>{
return name1 === name2 ? score2 - score1 : order.indexOf(name1) - order.indexOf(name2);
});
return sortedArr;
}
console.log("***Sorted Array***");
console.log(sortArray(data, order));
console.log("***Original Array***");
console.log(data);
I have an object containing an id as the key, and quantity as the value, e.g.;
let order = {1002: 2, 1010: 1}
I want to compare the order object with an array of products, e.g.:
let stock = [{name: "test", id: "1002", quantity: 100}, {name: "moreTest", id: "1010", quantity: 100}]
I want to reduce the quantity for each object in my stocks array, based on the according value in the stocks object, e.g.:
let newStock = [{name: "test", id: "1002", quantity: 98}, {name: "moreTest", id: "1010", quantity: 99}]
Here's a one-liner approach:
let newStock = stock.map(item => ({...item, quantity: item.quantity - (order[parseInt(item.id,10)] || 0)}))
BTW:
I think stock's id should be a number (or order needs to have a string key - I used parseInt but I don't think it's the best practice).
stock should be a map as well to prevent multiple stocks with the same id.
Here is a possible solution, very clear:
let order = {1002: 2, 1010: 1}
let stock = [{name: "test", id: "1002", quantity: 100}, {name: "moreTest", id: "1010", quantity: 100}]
stock.forEach(element => {
Object.keys(order).forEach(function(key) {
const quantityBought = order[key];
// == and not === because element.id is a string and key is a number
if (element.id == key) {
element.quantity -= quantityBought;
}
});
});
console.log(stock);
You can use Array.prototype.reduce here something Like this
let stock = [{name: "test", id: "1002", quantity: 100}, {name: "moreTest", id: "1010", quantity: 100}]
let order = {1002: 2, 1010: 1}
let result=stock.reduce((acc,value)=>{
value.quantity=value.quantity-order[value.id];
acc.push(value);
return acc;
},[])
console.log(result)
For reference look at array reduce function in mozilla docs
const censusMembers = Object.freeze([
{
id: 1,
name: 'Bob'
}, {
id: 2,
name: 'Sue'
}, {
id: 3,
name: 'Mary',
household_id: 2
}, {
id: 4,
name: 'Elizabeth',
household_id: 6
}, {
id: 5,
name: 'Tom'
}, {
id: 6,
name: 'Jill'
}, {
id: 7,
name: 'John',
household_id: 6
}
]);
In this array, A dependent can be determined by the presence of a household_id. The household_id is a reference to the ID of the employee that that member is a depended of (ex in the censusMembers list 'Mary' is a dependent of 'Sue')
How to build a function that takes in an id and the array of members(census members) and returns all dependents that belong to the user that has that id.
If the id is of a dependent, or isn't in the censusMember array then the function should return null.
If there are no dependents then the function should return an empty arrray.
for example:
if I give input as id 6
then output shoul be
[
{"id":4,"name":"Elizabeth","household_id":6},
{"id":7,"name":"John","household_id":6}
]
Here is code that seems to do what you would like:
const {curry, find, propEq, has, filter} = R
const householdMembers = curry((census, id) => {
const person = find(propEq('id', id), census);
return person
? has('household_id', person)
? null
: filter(propEq('household_id', id), census)
: null
})
var censusMembers = Object.freeze([
{id: 1, name: 'Bob'},
{id: 2, name: 'Sue' },
{id: 3, name: 'Mary', household_id: 2 },
{id: 4, name: 'Elizabeth', household_id: 6},
{id: 5, name: 'Tom'},
{id: 6, name: 'Jill'},
{id: 7, name: 'John', household_id: 6}
])
const householders = householdMembers(censusMembers)
console.log(householders(6))
//=> [
// {id: 4, name: 'Elizabeth','household_id': 6},
// {id: 7, name: 'John', 'household_id': 6}
// ]
console.log(householders(7)) //=> null
console.log(householders(8)) //=> null
console.log(householders(5)) //=> []
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>
But I would suggest that you might want to rethink this API. The empty array is a perfectly reasonable result when nothing is found. Making it return null for some of these cases makes the output much harder to use. For instance, if you wanted to retrieve the list of names of household members, you could simply write const householderNames = pipe(householders, prop('name')). Or you could do this if your function always returned a list.
Having a single function return multiple types like this is much harder to understand, and much, much harder to maintain. Note how much simpler the following version is, one that always returns a (possibly empty) list:
const members = curry((census, id) => filter(propEq('household_id', id), census))
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);