Convert and modify ruby hash to new hash - ruby

How could I convert my hash to 2 very different hashes?
My hash:
{ "grey"=> ["421_01.jpg", "421_02.jpg", "421_03.jpg"],
"heather blue"=> ["422_01.jpg", "422_02.jpg", "422_03.jpg"],
"indigo"=> [],
"dark grey"=> ["435_01.jpg", "435_02.jpg", "435_03.jpg"] }
1. Desired hash: (all values from my hash)
[{ src: "421_01.jpg" },
{ src: "421_02.jpg" },
{ src: "421_03.jpg" },
{ src: "422_01.jpg" },
{ src: "422_02.jpg" },
{ src: "422_03.jpg" },
{ src: "435_01.jpg" },
{ src: "435_02.jpg" },
{ src: "435_03.jpg" }]
2. Desired hash:
[{
image: "421_01.jpg, 421_02.jpg, 421_03.jpg",
attributes: [
{
name: "Color",
option: "grey"
}
]
},
{
image: "422_01.jpg, 422_02.jpg, 422_03.jpg",
attributes: [
{
name: "Color",
option: "heather blue"
}
]
},
{
image: "",
attributes: [
{
name: "Color",
option: "indigo"
}
]
},
{
image: "435_01.jpg, 435_02.jpg, 435_03.jpg",
attributes: [
{
name: "Color",
option: "dark grey"
}
]
}]
Please note: empty array in my hash shouldn't add any images to color variation, but should add variation with empty image string. (All shown in examples)

h = { "grey"=> ["421_01.jpg", "421_02.jpg", "421_03.jpg"],
"heather blue"=> ["422_01.jpg", "422_02.jpg", "422_03.jpg"],
"indigo"=> [],
"dark grey"=> ["435_01.jpg", "435_02.jpg", "435_03.jpg"] }
a = h.values
#=> [["421_01.jpg", "421_02.jpg", "421_03.jpg"],
# ["422_01.jpg", "422_02.jpg", "422_03.jpg"],
# [],
# ["435_01.jpg", "435_02.jpg", "435_03.jpg"]]
For #1:
a.flat_map { |s| { src: s } }
#=> [{:src=>"421_01.jpg"}, {:src=>"421_02.jpg"}, {:src=>"421_03.jpg"},
# {:src=>"422_01.jpg"}, {:src=>"422_02.jpg"}, {:src=>"422_03.jpg"},
# {:src=>"435_01.jpg"}, {:src=>"435_02.jpg"}, {:src=>"435_03.jpg"}]
For #2:
h.map { |k,_| { image: a.shift.join(', '),
attributes: [{ name: "Color", option: k }] } }
# => [{:image=>"421_01.jpg, 421_02.jpg, 421_03.jpg",
:attributes=>[{:name=>"Color", :option=>"grey"}]},
{:image=>"422_01.jpg, 422_02.jpg, 422_03.jpg",
:attributes=>[{:name=>"Color", :option=>"heather blue"}]},
{:image=>"", :attributes=>[{:name=>"Color", :option=>"indigo"}]},
{:image=>"435_01.jpg, 435_02.jpg, 435_03.jpg",
:attributes=>[{:name=>"Color", :option=>"dark grey"}]}]

Related

How loop and read JSON data in cypress

Suppose that you have a JSON file that contains data like this:
[
{
name: 'Data Groups',
},
{
name: 'Transaction start Filter',
},
{
name: 'Filter',
},
{
name: 'Graph, Tables',
},
{
name: 'Trending with filters',
},
{
name: 'Graph, area & Pie',
},
]
How to read it using cypress and print the name one by one using cypress?
You can do something like this:
var arr = [
{
name: 'Data Groups',
},
{
name: 'Transaction start Filter',
},
{
name: 'Filter',
},
{
name: 'Graph, Tables',
},
{
name: 'Trending with filters',
},
{
name: 'Graph, area & Pie',
},
]
for (var index in arr) {
cy.log(arr[index].name)
}
Test Runner Screenshot:
If you want to read from a JSON file which is present somewhere in your repo you can:
//If the file is in fixtures folder
cy.fixture('data.json').then((data) => {
for (var index in data) {
cy.log(data[index].name)
}
})
//If the file is somewhere else in repo
cy.fixture('path to file/data.json').then((data) => {
for (var index in data) {
cy.log(data[index].name)
}
})
If this is your test data, you can iterate over the array and dynamically create a test case for each object in the array:
[
{
"name": "Data Groups"
},
{
"name": "Transaction start Filter",
},
{
"name": "Filter",
},
{
"name": "Graph, Tables",
},
{
"name": "Trending with filters",
},
{
"name": "Graph, area & Pie",
}
].forEach(data => {
it(`Test ${JSON.stringify(data)}`, () => {
cy
.log(data.name);
});
});
And the result from test runner:
Just beware that you left out one bracket, so your data is not a valid JSON.

How to merge array of hashes based on hash value but not merge values instead override

I have an array of hashes like this:
[
{ name: 'Pratha', email: 'c#f.com' },
{ name: 'John', email: 'j#g.com' },
{ name: 'Clark', email: 'x#z.com' },
]
And this is second group array of hashes:
[
{ name: 'AnotherNameSameEmail', email: 'c#f.com' },
{ name: 'JohnAnotherName', email: 'j#g.com' },
{ name: 'Mark', email: 'd#o.com' },
]
What I want is, merge these two arrays into one, merge based on :email and keep latest (or first) :name.
Expected Result (latest name overrided):
[
{ name: 'AnotherNameSameEmail', email: 'c#f.com' },
{ name: 'JohnAnotherName', email: 'j#g.com' },
{ name: 'Mark', email: 'd#o.com' },
{ name: 'Clark', email: 'x#z.com' },
]
or (first name preserved)
[
{ name: 'Pratha', email: 'c#f.com' },
{ name: 'John', email: 'j#g.com' },
{ name: 'Mark', email: 'd#o.com' },
{ name: 'Clark', email: 'x#z.com' },
]
So, basically, I want to group by :email, retain one :name, drop dupe emails.
The examples found on SO is creates an array of values for :name.
Ruby 2.6.3
Maybe you could just call Array#uniq with a block on email key of the concatenation (Array#+) of the two arrays:
(ary1 + ary2).uniq { |h| h[:email] }
a1 = [
{ name: 'Pratha', email: 'c#f.com' },
{ name: 'John', email: 'j#g.com' },
{ name: 'Clark', email: 'x#z.com' },
]
a2 = [
{ name: 'AnotherNameSameEmail', email: 'c#f.com' },
{ name: 'JohnAnotherName', email: 'j#g.com' },
{ name: 'Mark', email: 'd#o.com' },
]
Let's first keep the last:
(a1+a2).each_with_object({}) { |g,h| h.update(g[:email]=>g) }.values
#=> [{:name=>"AnotherNameSameEmail", :email=>"c#f.com"},
# {:name=>"JohnAnotherName", :email=>"j#g.com"},
# {:name=>"Clark", :email=>"x#z.com"},
# {:name=>"Mark", :email=>"d#o.com"}]
To keep the first, do the same with (a1+a2) replaced with (a2+a1), to obtain:
#=> [{:name=>"Pratha", :email=>"c#f.com"},
# {:name=>"John", :email=>"j#g.com"},
# {:name=>"Mark", :email=>"d#o.com"},
# {:name=>"Clark", :email=>"x#z.com"}]

Can an array be used as a d3 nest key?

I have a database of movies that I would like to nest by genre. The problem is that each movie can have multiple genres. So if I have several movies formatted like so
[
{
title : 'foo',
genres : ['Action', 'Comedy', 'Thriller']
},{
title : 'bar',
genres : ['Action']
}
]
I'd like to nest them by each individual genre so that the result would be
[
{
key: 'Action',
values: [ { title: 'foo' }, { title: 'bar'} ]
},{
key: 'Comedy',
values: [ { title: 'foo' } ]
},{
key: 'Thriller',
values: [ { title: 'foo' } ]
}
]
not directly, but you can expand your array
For example:
jj = [{ genre: ['thriller', 'comedy'], title: 'foo'}, { genre: ['thriller', 'action'], title: 'papa'}]
to expand your array:
jj2 = []
jj.forEach(function(movie) { movie.genre.forEach( function(single_genre) { jj2.push({ language: movie.language, genre: single_genre, title: movie.title } ); } ); })
Then you can perform your nesting as normal:
d3.nest().key(function(d) { return d.genre; }).entries(jj2)

ng-repeat orderby of dynamic lenght of object

I have the following object that contains 2 fixed attributes (OrderId and Purchasedate, and an array of attribues. I try to to put this in ng-repeat with orderBy option. The first 2 attribute (OrderId and PurchaseDate) work OK when sorting is applied by clicking on the header. However I do not get it working on the 3 rd attribute and so on.
The rows shown on the table are correct.
The object looks like
e.g.
$scope.orders;
[
{ OrderId: "P888291", PurchaseDate : "2016-12-02",
Items: { elt : [ { Name: "City", Value: "New York"}, { Name: "Street", Value: "Broadway 5" }, { Name: "Country", Value: "USA" } ] } },
{ OrderId: "P334498", PurchaseDate : "2016-11-02",
Items: { elt : [ { Name: "City", Value: "London" }, { Name: "Street", Value: "WestMinister 3" }, { Name: "Country", Value: "Great Brittain" } ] } },
{ OrderId: "G393383", PurchaseDate : "2016-11-28",
Items: { elt : [ { Name: "City", Value: "Milan" }, { Name: "Street", Value: "Pizza 8" }, { Name: "Country", Value: "Italy" } ] } },
{ OrderId: "P978381", PurchaseDate : "2015-05-25",
Items: { elt : [ { Name: "City", Value: "Seattle" }, { Name: "Street", Value: "Houston 9" }, { Name: "Country", Value: "US" } ] } },
{ OrderId: "P983394", PurchaseDate : "2015-06-05",
Items: { elt : [ { Name: "City", Value: "Amsterdam" }, { Name: "Street", Value: "Damrak 5" }, { Name: "Country", Value: "Netherlands" } ] } },
{ OrderId: "G678994", PurchaseDate : "2015-04-01",
Items: { elt : [ { Name: "City", Value: "The Hague" }, { Name: "Street", Value: "Markt 22" }, { Name: "Country", Value: "Netherlands" } ] } },
{ OrderId: "P128994", PurchaseDate : "2016-12-04",
Items: { elt : [ { Name: "City", Value: "The Hague" }, { Name: "Street", Value: "Plein 7" }, { Name: "Country", Value: "Netherlands" } ] } },
];
Please advise and the code is put in :
http://www.w3schools.com/code/tryit.asp?filename=FAG7BWVK8BYH
You can try with custom filter logic.(https://docs.angularjs.org/api/ng/filter/orderBy )
for example:
JS:
$scope.filterOrderFn = function(orderobj)
{
// Do
if(...)
{
return _something_// this will be your sorted order according to your first condition
}
else if(...)
{
return _something_ // this will be your sorted order according to your second condition if require
}
return false; // otherwise it won't be within the orderlist
};
HTML:
...
<article data-ng-repeat="order in orders | orderBy:filterOrderFn" class="order">
...
If you need a very specific ordering behaviour you could write your own filter (although orderBy should be enough for most uses cases). As you may know you can chain many filters together, so adding your custom filter function doesn't force you to remove the previous filter using the search object (they will work together seamlessly).

Multiple Filter on kendogrid not working

I'm trying to put 2 filters on kendo grid with 'OR' logic.
It's not working. I need the grid to be filtered with the both the dropdowns.
If in Foo dropdown 'foo1' is selected and in Bar dropdown 'All' selected, then the grid should be displaying
foo bar
1 1
1 2
Code below:
$(function() {
var grid=$("#grid").kendoGrid({
dataSource: {
data: [
{ foo: "1", bar: "1" },{ foo: "1", bar: "2" },
{ foo: "2", bar: "2" },{ foo: "2", bar: "1" },
{ foo: "3", bar: "3" },{ foo: "3", bar: "2" }
]
},
columns: [
"foo","bar"
],
toolbar: kendo.template($("#template").html())
});
grid.find("#foo").kendoDropDownList({
dataTextField: "name",
dataValueField: "id",
autoBind: false,
optionLabel: "All",
dataSource: [{id:'1', name:'foo1'}, {id:'2', name:'foo2'},{id:'3', name:'foo3'}],
change: function () {
var ds = $("#grid").data("kendoGrid").dataSource;
var filter = {
logic: "and",
filters: []
};
if (this.value()) {
filter.filters.push([{ field: "bar", operator: "eq", value:
$("#bar").data('kendoDropDownList').value() },
{ field: "foo", operator: "eq", value: $("#foo").data('kendoDropDownList').value() }
]);
}
ds.filter([filter]);
}
});
grid.find("#bar").kendoDropDownList({
dataTextField: "name",
dataValueField: "id",
autoBind: false,
optionLabel: "All",
dataSource: [{id:'1', name:'bar1'}, {id:'2', name:'bar2'},{id:'3', name:'bar3'}],
change: function () {
var ds = $("#grid").data("kendoGrid").dataSource;
var filter = {
logic: "and",
filters: []
};
if (this.value()) {
filter.filters.push([{ field: "bar", operator: "eq", value:
$("#bar").data('kendoDropDownList').value() },
{ field: "foo", operator: "eq", value: $("#foo").data('kendoDropDownList').value() }
]);
}
ds.filter([filter]);
}
});
});
After pushing the filters to the filter array the grid datasource is not filtered.
Updated jsbin below:
http://jsbin.com/izuloj/23/edit
There are couple of problems:
The argument for DataSource filters is an array but you are pushing an array when you do:
filter.filters.push([
{ field: "bar", operator: "eq", value: $("#bar").data('kendoDropDownList').value() },
{ field: "foo", operator: "eq", value: $("#foo").data('kendoDropDownList').value() }
]);
Comparing with empty is not the same that not adding the condition. So you should actually do:
// If there is some value in "bar" we add a condition for filtering it
if ($("#bar").data('kendoDropDownList').value()) {
filter.filters.push({
field: "bar",
operator: "eq",
value: $("#bar").data('kendoDropDownList').value() }
);
}
// If there is some value in "foo" we add a condition for filtering it
if ($("#foo").data('kendoDropDownList').value()) {
filter.filters.push(
{
field: "foo",
operator: "eq",
value: $("#foo").data('kendoDropDownList').value() }
);
}
Finally, you should not set any filter if both drop down inputs are empty but in that case you cannot send an empty filter array, you should do ds.filter({})) instead.
So your change function ends being:
function onChange () {
var ds = $("#grid").data("kendoGrid").dataSource;
var filtering = false;
var filter = {
logic: "and",
filters: []
};
if ($("#bar").data('kendoDropDownList').value()) {
filtering = true;
filter.filters.push(
{ field: "bar", operator: "eq", value: $("#bar").data('kendoDropDownList').value() }
);
}
if ($("#foo").data('kendoDropDownList').value()) {
filtering = true;
filter.filters.push(
{ field: "foo", operator: "eq", value: $("#foo").data('kendoDropDownList').value() }
);
}
if (filtering) {
ds.filter([filter]);
} else {
ds.filter({});
}
}
Your code modified here : http://jsbin.com/izuloj/31/edit
Instead of
var filter = {
logic: "and",
filters: []
};
if (this.value()) {
filter.filters.push([{ field: "bar", operator: "eq", value:
$("#bar").data('kendoDropDownList').value() },
{ field: "foo", operator: "eq", value: $("#foo").data('kendoDropDownList').value() }
]);
}
ds.filter([filter]);
Doing below works....easy to read
if ( $("#foo").data('kendoDropDownList').value() === "")
{
ds.filter( { field: "bar", operator: "eq", value: $("#bar").data('kendoDropDownList').value()});
}
else
{
ds.filter({
logic: "and",
filters: [{ field: "foo", operator: "eq", value: $("#foo").data('kendoDropDownList').value() },{ field: "bar", operator: "eq", value: $("#bar").data('kendoDropDownList').value() }]
});
}
Remove the square brackets on ds.filter([filter]); so that it becomes ds.filter(filter);.

Resources