I'm looking for the most concise way to convert an array to an object while plucking the field used as the key for each result.
This is the solution I found, and I'm wondering if there's an easier way.
r.table('product').fold({}, function(products, product) {
return products.merge(
r.object(
product('id').coerceTo('string'),
product.without('id')
)
);
})
Thanks!
Example
// Input:
[{ id: 0, price: 19.99 }, { id: 1, price: 24.99 }]
// Output:
{ "0": { price: 19.99 }, "1": { price: 24.99 } }
I'm not an expert, but AFAIK, using fold() directly on a table will "stop subsequent commands from being parallelized":
https://www.rethinkdb.com/docs/optimization/
So, if I'm not mistaken, if the order is not important it might be better to choose reduce() over fold(), e.g.
table.map(function(row) {
return r.object(
row('id').coerceTo('string'), row.without('id')
)
})
.reduce(function(left, right) {
return left.merge(right)
})
Related
just wondering if someone could point me in the right direction of .map functionality. This is unfortunately something I'm struggling to get my head around.
If I had an object, lets say the following:
const myPetsAndFood = {
pets:[
{
species: "dog",
breed: "Labrador",
age: 12
},
{
species: "cat",
breed: "unknown",
age: 7,
},
{
species: "fish",
breed: "goldfish",
age: 1,
}
],
food: [
{
dogfood: 15.00,
},
{
catfood: 11.00,
},
{
fishfood: 4.00,
}
],
};
Could anyone explain how I'd utilise .map to obtain the data values of age and price if possible please?
A brief explanation or example is more than suffice, I'd appreciate any time/input possible. In all probability, I'll be sat here reading and trying to figure it out in the mean time.
If you got this far - Thank you for your time.
So the .map can only be used with arrays. This way you can not do something similar to:
myPetsAndFood.map()
Let's say you want do console.log the age. You would have to get the array first. So:
myPetsAndFood.pets.map((pet) => {
console.log(pet.age)
})
And it would print 12, followed by 7 followed by 1. If you want to store it inside an array you can create an array and use .push("//infos wanted to be pushed//")
Object.keys(myPetsAndFood).map(function(key, index) {
console.log(myPetsAndFood[key][0].dogfood);
console.log(myPetsAndFood[key][0].age);
});
You are going to have to figure out a way to replace the 0 with some sort of counter that will increment.
map is a method of arrays, it doesn't exist on objects. You could use it on the arrays within the object ( myPetsAndFood.pets.map( /* ... */ ) ) but you'd have to use a for loop or some other technique to parse each item in the object.
An example of how to use the map function for one of your arrays:
const agesArray = myPetsAndFood.pets.map((item) => {
return item.age;
});
So you have imbricated arrays here. This makes it so you have to go into your wanted array first before being able to execute your map.
For example: myPetsAndFood.pets.map(function)
The way that .map works is it executes your function on every element in your array and returns an array with the equivalency(source).
Therefore, in order to get the age of every pet, you have to tell your function to get your age property of your objects.
For example: myPetsAndFood.pets.map((pet) => pet.age)
This will return an array containing only the age of every one of your pets.
Now the problem with this is your second array. We cannot call the .map function on that array because your different properties don't have the same name. Therefore, your .map won't have any common ground to return a sensible array.
We can fix this issue by splitting your one variable into two: name and price for example. After this change, we can now call the .map on your array properly by telling it which property you need.
For example: myPetsAndFood.foods.map((food) => food.price)
Below is a full code snippet which should show off the above description.
const myPetsAndFood = {
pets:[
{
species: "dog",
breed: "Labrador",
age: 12
},
{
species: "cat",
breed: "unknown",
age: 7,
},
{
species: "fish",
breed: "goldfish",
age: 1,
}
],
foods: [
{
name: "dog",
price: 15.00,
},
{
name: "cat",
price: 11.00,
},
{
name: "fish",
price: 4.00,
}
],
};
const catAge = myPetsAndFood.pets.map((pet) => pet.age)
const foodPrice = myPetsAndFood.foods.map((food) => food.price)
console.log(catAge)
console.log(foodPrice)
I have an arrry that has 100 object and it has same property code
Data = [
{yera:"2019", name:"saif", topic:"oil"},
{yera:"2018", name:"abc", topic: "oil"},
{yera:"2018", name:"jorj", topic:"energy"},
{yera:"2017", name:"tom", topic:"gas"},
{yera:"2016",name:"saif",topic:"electricity "},
{yera:"2014", name:"gour",topic:"oil"},
Assuming you want to remove duplicates from the array of objects based on a key of that object, the code below will achieve that.
var data = [
{yera:"2019", name:"saif", topic:"oil"},
{yera:"2018", name:"abc", topic: "oil"},
{yera:"2018", name:"jorj", topic:"energy"},
{yera:"2017", name:"tom", topic:"gas"},
{yera:"2016",name:"saif",topic:"electricity "},
{yera:"2014", name:"gour",topic:"oil"}
]
function getUniqueData(originalData, keyToCheckForUniqueness) {
var store = {};
var output = [];
originalData.forEach(function (ob) {
var val = ob[keyToCheckForUniqueness];
if (!store[val]) {
store[val] = [ob];
} else {
store[val].push(ob);
}
});
// at this point your store contains all the repeating data based on that property value
// console.log(store);
// now emit single values from that store;
// this logic can be based on any criterion, I chose the first element of the array - it may change depending on the order of values in input
Object.keys(store).forEach(function (key) {
var uniqueValArray = store[key];
var uniqueVal = uniqueValArray[0]; // take the first entry
output.push(uniqueVal);
});
return output;
}
getUniqueData(data, "topic");
This will achieve what I think you want to figure out. A word of advice - Don't let people think when you ask them for help. Second, try writing the logic for yourself. Post your non-working solution and ask, where you made a mistake - rather than asking. Given your rep, welcome to SO. Hope you a great learning experience.
Assuming, you want unique values for a given property of the objects, you could map that value and take a Set for getting unique values.
function getUnique(array, key) {
return Array.from(new Set(array.map(({ [key]: v }) => v)));
}
var array = [{ year: "2019", name: "grace", topic: "oil" }, { year: "2018", name: "grace", topic: "oil" }, { year: "2018", name: "jane", topic: "energy" }, { year: "2017", name: "tom", topic: "gas" }, { year: "2016", name: "jane", topic: "electricity" }, { year: "2014", name: "gour", topic: "oil" }];
console.log(getUnique(array, 'year'));
console.log(getUnique(array, 'name'));
console.log(getUnique(array, 'topic'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have an object array as follows:
products = [
{
id: 1,
title: "Product 1",
specifications: {
price: 1.55,
discount: 15,
attributes: [
{
l1: 100,
l2: 80
height:200,
weight: 15,
parameters: [
{
id: 199199 // this is how I identify the parameter
size: 185 // this is what I want to change
}, ...
]
}, ...
]
}
}, ...
]
... and an array of changes to parameters I want to apply, for example: change size to 189 where product.specifications.attributes.parameters.id == 199199.
I'd like to do this without flattening any elements as they are part of a Vue.js data structure, it will break the reactivity.
How could I do this? I am open to using Underscore or lo-dash
This looks ugly, but it is effective:
To make it more dynamic, let's use variables: identifier will be your '199199' value and new_size for the '189' value.
methods: {
updateRecord: function(identifier, new_size) {
this.products.map(function(product) {
product.specifications.attributes.map(function(attribute) {
attribute.parameters.map(function(parameter) {
if (parameter.id == identifier) parameter.size = new_size;
})
});
});
}
}
Here is a working fiddle: https://jsfiddle.net/crabbly/eL7et9e8/
_.forEach(products, function(product) {
_.forEach(_.get(product, 'specifications.attributes', []), function(attribute) {
_.set(_.find(attribute.parameters, {id: 199199}), 'size', 189);
});
});
I believe what you want is underscore's findIndex() - http://underscorejs.org/#findIndex. Once you find which element in the array you want to apply the changes to (comparing the nested id to what you are looking for) you can then make the change to that particular element.
I want to use linq.js to group the following data by date.
data2 = [{
"date": 1399298400.0,
"adId": 1057946139383,
"impressions": 1000000
}, {
"date": 1399298400.0,
"adId": 3301784671323,
"impressions": 535714
}...... etc.
];
Here's my attempt:
var linq = Enumerable.From(data2);
data2 = linq.GroupBy(function (x) {
return x.date;
}).Select(function (x) {
return {
date: x.Key(),
impressions: x.Sum(function (y) {
return y.impressions | 0;
})
};
}).ToArray();
However, It's not working correctly because the sum of all the impressions before and after the GroupBy are close but not identical.
What is the correct way to use group by in linq.js in this case?
Here's an example in fiddle with full dataset here which alerts the total impressions before and after using the GroupBy.
Solution
You can do this by passing a callback as the third parameter like this:
var grouped = Enumerable.from(dataArray).groupBy("$.person", null, (key, g) => {
return {
person: key,
likes: g.sum("$.likes | 0")
}
}).toArray()
Explanation
In groupBy, the third parameter allows you to modify the results before emitting:
In JS, the bitwise or operator (a single pipe |) returns the first value if it exists, otherwise it returns the second one. Without it, trying to sum an undefined value with a real one, will return NaN
undefined + 1 // NaN
Without | 0, the result would look like this:
This example uses shorthand syntax, but if you prefer anytime you see a string with a dollar sign, you can replace it with the lambda syntax like this (they both do the same exact thing):
// Shorthand
.Select("$.impressions")
// Lambda
.Select(function (x) { return x.impressions })
Working demo with Stack Snippets:
var dataArray = [
{
person: "james",
likes: 100
},
{
person: "james",
likes: 250
},
{
person: "kyle",
likes: 300
},
{
person: "kyle"
//,likes: 450
}
];
var grouped = Enumerable.from(dataArray).groupBy("$.person", null, (key, g) => {
return { person: key, likes: g.sum("$.likes | 0") }
}).toArray()
console.log(grouped);
<script src="https://unpkg.com/linq#3.2.0/linq.js"></script>
Further Reading:
SO - linqjs group by with a sum
I am the author of the open source project http://www.jinqJs.com. You can easily do that in jinqJs like this:
jinqJs().from(data2).groupBy('date').sum('impressions').select();
Let me know if I can be of anymore help.
You might try to group by date.toString(). Might be safer due to how JS evaluates dates equality
Alternatively, people coming into this question might have zero to a lot of buy in using linq.js.
If you're already pulling it in, go for it, but if this is the first couple real use cases for it, it's worth noting that you can accomplish the same thing in vanilla js:
For this data:
var dataArray = [
{ person: "james", likes: 100 },
{ person: "james", likes: 250 },
{ person: "kyle", likes: 300 },
{ person: "kyle" }
];
You can build an object with properties for each key / person and keep summing the values of those props
var obj = dataArray.reduce((acc, cur) => {
acc[cur.person] = (acc[cur.person] || 0) + (cur.likes || 0)
return acc
}, {})
If you want that to be an array of objects, you can convert from an object to array like this
var array = Object.entries(obj).map(entry => {
return { person: entry[0], likes: entry[1] }
})
Can someone explain in simple terms how reduce function with its arguments reduceAdd, reduceSum, reduceRemove works in crossfilter?
Remember that map reduce reduces a dataset by keys of a particular dimension. For example lets use a crossfilter instance with records:
[
{ name: "Gates", age: 57, worth: 72000000000, gender: "m" },
{ name: "Buffet", age: 59, worth: 58000000000, gender: "m" },
{ name: "Winfrey", age: 83, worth: 2900000000, gender: "f" },
{ name: "Bloomberg", age: 71, worth: 31000000000, gender: "m" },
{ name: "Walton", age: 64, worth: 33000000000, gender: "f" },
]
and dimensions name, age, worth, and gender. We will reduce the gender dimension using the reduce method.
First we define the reduceAdd, reduceRemove, and reduceInitial callback methods.
reduceInitial returns an object with the form of the reduced object and the initial values. It takes no parameters.
function reduceInitial() {
return {
worth: 0,
count: 0
};
}
reduceAdd defines what happens when a record is being 'filtered into' the reduced object for a particular key. The first parameter is a transient instance of the reduced object. The second object is the current record. The method will return the augmented transient reduced object.
function reduceAdd(p, v) {
p.worth = p.worth + v.worth;
p.count = p.count + 1;
return p;
}
reduceRemove does the opposite of reduceAdd (at least in this example). It takes the same parameters as reduceAdd. It is needed because group reduces are updated as records are filtered and sometimes records need to be removed from a previously computed group reduction.
function reduceRemove(p, v) {
p.worth = p.worth - v.worth;
p.count = p.count - 1;
return p;
}
Invoking the reduce method would look like this:
mycf.dimensions.gender.reduce(reduceAdd, reduceRemove, reduceInitial)
To take a peek at the reduced values, use the all method. To see the top n values use the top(n) method.
mycf.dimensions.gender.reduce(reduceAdd, reduceRemove, reduceInitial).all()
The returned array would (should) look like:
[
{ key: "m", value: { worth: 161000000000, count: 3 } },
{ key: "f", value: { worth: 35000000000, count: 2 } },
]
The goals of reducing a dataset is to derive a new dataset by first grouping records by common keys, then reducing a dimension those groupings into a single value for each key. In this case we grouped by gender and reduced the worth dimension of that grouping by adding the values of records that shared the same key.
The other reduceX methods are convience methods for the reduce method.
For this example reduceSum would be the most appropriate replacement.
mycf.dimensions.gender.reduceSum(function(d) {
return d.worth;
});
Invoking all on the returned grouping would (should) look like:
[
{ key: "m", value: 161000000000 },
{ key: "f", value: 35000000000 },
]
reduceCount will count records
mycf.dimensions.gender.reduceCount();
Invoking all on the returned grouping would (should) look like:
[
{ key: "m", value: 3 },
{ key: "f", value: 2 },
]
Hope this helps :)
Source: https://github.com/square/crossfilter/wiki/API-Reference
http://blog.rusty.io/2012/09/17/crossfilter-tutorial/
var livingThings = crossfilter([
// Fact data.
{ name: “Rusty”, type: “human”, legs: 2 },
{ name: “Alex”, type: “human”, legs: 2 },
{ name: “Lassie”, type: “dog”, legs: 4 },
{ name: “Spot”, type: “dog”, legs: 4 },
{ name: “Polly”, type: “bird”, legs: 2 },
{ name: “Fiona”, type: “plant”, legs: 0 }
]);
For example, how many living things are in my house?
To do this, we’ll call the groupAll convenience function, which selects all
records into a single group, and then the reduceCount function, which
creates a count of the records.
// How many living things are in my house?
var n = livingThings.groupAll().reduceCount().value();
console.log("There are " + n + " living things in my house.") // 6
Now let’s get a count of all the legs in my house. Again, we’ll use the groupAll function to get all records in a single group, but then we call the
reduceSum function. This is going to sum values together. What values?
Well, we want legs, so let’s pass a function that extracts and returns the number of legs from the fact.
// How many total legs are in my house?
var legs = livingThings.groupAll().reduceSum(function(fact) {
return fact.legs;
}).value()
console.log("There are " + legs + " legs in my house.")
reduceCount function creates a count of the records.
reduceSum function is the sum values of these records.