Sorting an array of objects alphabetically - arrays

I am trying to sort an array of object alphabetically , to make things simple I am using the below example. In my typscript I do splice to insert and remove items from array object.
Array
cars = [{
id: 1,
items: [{
name: 'car1',
description: 'this is car1 description'
},{
name: 'car2',
description: 'this is car2 description'
},{
name: 'car3',
description: 'this is car3 description'
},{
name: 'car4',
description: 'this is car4 description'
},{
name: 'car5',
description: 'this is car5 description'
}]
}];
html
<p-dataView [value]="cars" [paginator]="true" [rows]="5">
<p-header>List of Cars</p-header>
<p-footer>Choose from the list.</p-footer>
<ng-template let-car pTemplate="listItem">
<p-fieldset legend="Header" *ngIf="car.id === 1" [toggleable]="true">
<div *ngFor="let _car of car.items">
{{_car.name}} - {{_car.description}}
</div>
</p-fieldset>
</ng-template>
</p-dataView>
**********************************************UPDATE********************************
Sorry I have one more layer
cars = [{
id: 1,
items: [{
Model:[{
name: 'car1',
description: 'this is car1 description'
}],
Model:[{
name: 'car2',
description: 'this is car2 description'
}],
},
id:2,
items: [{
Model:[{
name: 'car13,
description: 'this is car3 description'
}],
Model:[{
name: 'car4',
description: 'this is car4 description'
}],
}]
}];
I tried
cars[0].items.Model.sort((a,b) => a[0].name > b[0].name ? 1 : -1) //It did not work
also tried
cars[0].items.Model.sort(function (a, b) {
var nameA = a.name.toLowerCase();
var nameB = b.name.toLowerCase();
if (nameA < nameB) //sort string ascending
return -1
})

cars = [{
id: 1,
items: [{
name: 'ab',
description: 'this is car1 description'
},{
name: 'cd',
description: 'this is car2 description'
},{
name: 'car3',
description: 'this is car3 description'
},{
name: 'aaa',
description: 'this is car4 description'
},{
name: 'car5',
description: 'this is car5 description'
}]
}];
cars[0].items.sort((a,b) => a.name > b.name ? 1 : -1)

Or we can simplify to:
cars[0].items.sort((a,b) => a.name.localeCompare(b.name))

I find it most efficient to use a library like Underscore.js for utility functions like this:
Underscore.js and specifically the sortBy method.
Add underscorejs to your project and import it into your component.
import * as _ from 'underscore';
Then call the sort method and pass the array of objects and the key to sort on.
_.sortBy(car.items, 'name');
When you add or remove an item from the car array, resort the car.items collection and reassign it to car
car.items = _.sortBy(car.items, 'name');
Then you can display as you are now, with the sorted data.

naive sort with Array.prototype.sort()
cars.sort(function(x,y) {
if (x.items.name < y.items.name) return -1;
if (x.items.name > y.items.name) return 1;
return 0;
})

Related

Angular filter array inside array

I'm working in an Angular 9 app and I need to filter an array based on another array inside each object. To understand me here is an example of the array
const products = [
{
name: 'Product 1',
origins: [
{ quantity: 1, name: 'Pack 1' },
{ quantity: 1, name: 'Pack 2' },
]
},
{
name: 'Product 2',
origins: [
{ quantity: 1, name: 'Pack 1' },
{ quantity: 1, name: 'Pack 2' },
]
},
{
name: 'Product 3',
origins: [
{ quantity: 1, name: 'Inventory' },
{ quantity: 1, name: 'Pack 5' },
]
}
]
So I got a filter input which has to filter the products by a coincidence on the name of the product or one or more origin's name.
For example, if I type "2" the result array must be:
products = [
{
name: 'Product 1',
origins: [
{ quantity: 1, name: 'Pack 2' },
]
},
{
name: 'Product 2',
origins: [
{ quantity: 1, name: 'Pack 2' },
]
}
]
Because the character "2" is in the name of origin of Product 1 and Product 2, also is present in the name "Product 2"
I tried many things to do this but the result array always modifies my original array when I put this in a pipe
<input type="text" [(ngModel)]="searchtext">
<div *ngFor="let p of (saleProducts | filter : searchtext); let i = index">
{{ p.name }}
<div *ngIf="p.origins.length > 0">
<div *ngFor="let o of p.origins">
{{ o.name }}
</div>
</div>
</div>
What is the best (simple and optimized) way to filter this using a pipe without modifying the original array?
below a pipe implementation to with a default key origins:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'filter'
})
export class FilterPipe implements PipeTransform {
transform(value: any[], searchName: string, key = 'origins'): any[] {
const products = []
value.forEach(product => {
const matches = product[key].filter(({ name }) => name === searchName)
if (matches.length) {
products.push({ ...product, [key]: matches })
}
})
return products
}
}

Is there other solution to use `R.applySpec` without inserting the unchanged keys value?

Is there other solution to use R.applySpec without inserting the the unchanged keys value?(without needs to type id and name keys in the example, because later the keys will be change dynamically). Thank you.
Here is my input data
const data = [
[
{ id: 'data1', name: 'it is data 1', itemId: 'item1' },
{ id: 'data1', name: 'it is data 1', itemId: 'item2' }
],
[
{ id: 'data2', name: 'it is data 2', itemId: 'item1' }
],
[
{ id: 'data3', name: 'it is data 3', itemId: 'item1' },
{ id: 'data3', name: 'it is data 3', itemId: 'item2' }
]
]
And the output
[
{
id: 'data1', // this one doesn't change
name: 'it is data 1', // this one doesn't change
itemId: [ 'item1', 'item2' ]
},
{
id: 'data2', // this one doesn't change
name: 'it is data 2', // this one doesn't change
itemId: [ 'item1' ]
},
{
id: 'data3', // this one doesn't change
name: 'it is data 3', // this one doesn't change
itemId: [ 'item1', 'item2' ]
}
]
The solution to get the output using Ramda
const result = R.map(
R.applySpec({
id: R.path([0, 'id']),
name: R.path([0, 'name']), // don't need to type id or name again
itemId: R.pluck('itemId')
})
)(data)
We could certainly write something in Ramda like this:
const convert = map (lift (mergeRight) (head, pipe (pluck ('itemId'), objOf('itemId'))))
const data = [[{id: 'data1', name: 'it is data 1', itemId: 'item1'}, {id: 'data1', name: 'it is data 1', itemId: 'item2'}], [{id: 'data2', name: 'it is data 2', itemId: 'item1'}], [{id: 'data3', name: 'it is data 3', itemId: 'item1'}, {id: 'data3', name: 'it is data 3', itemId: 'item2'}]]
console .log (convert (data))
.as-console-wrapper {min-height: 100% !important; top: 0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script> const {map, lift, mergeRight, head, pipe, pluck, objOf} = R </script>
I'm not sure whether I find that more or less readable than a ES6/Ramda version, though:
const convert = map (
reduce ((a, {itemId, ...rest}) => ({...rest, itemId: [...(a .itemId || []), itemId]}), {})
)
or a plain ES6 version:
const convert = data => data .map (
ds => ds .reduce (
(a, {itemId, ...rest}) => ({...rest, itemId: [...(a .itemId || []), itemId]}),
{}
)
)
The question about applySpec is interesting. This function lets you build a new object out of the old one, but you have to entirely describe the new object. There is another function, evolve, which keeps intact all the properties of the input object, replacing only those specifically mentioned, by applying a function to their current value. But the input to the functions in evolve accepts only the current value, unlike applySpec which has access to the entire original object.
I could see some rationale for a function combining these behaviors. But I don't have a clear API in my head for how it should work. If you have some thoughts on this, and want to make a proposal, the Ramda team is always looking for suggestions.

Ruby: using an array of hashes to select values from another array of hashes

Say I have two arrays of hashes:
array_1 = [{name: "Dale Cooper", role: "author"},
{name: "Lucy Moran", role: "author"},
{name: "Harry Truman", role: "author"}]
array_2 = [{author: "Lucy Moran", title: "Lorem"},
{author: "Bobby Briggs", title: "Ipsum"},
{author: "Harry Truman", title: "Dolor"}]
How would I go about selecting from array_2 just the hashes from authors that are in array_1? Preferably, the result would be this:
array_3 = [{author: "Lucy Moran", title: "Lorem"},
{author: "Harry Truman", title: "Dolor"}]
You could save all the array_1 names in a set in order to select hashes from array_2 :
require 'set'
array_1 = [{ name: 'Dale Cooper', role: 'author' },
{ name: 'Lucy Moran', role: 'author' },
{ name: 'Harry Truman', role: 'author' }]
array_2 = [{ author: 'Lucy Moran', title: 'Lorem' },
{ author: 'Bobby Briggs', title: 'Ipsum' },
{ author: 'Harry Truman', title: 'Dolor' }]
authors = Set.new(array_1.map{ |h| h[:name] })
array_3 = array_2.select{ |h| authors.include?(h[:author]) }
# [{:author=>"Lucy Moran", :title=>"Lorem"},
# {:author=>"Harry Truman", :title=>"Dolor"}]

filter the second selectbox based on the first selected option using angular js

i have 2 select boxes,which is getting the data using ng-options and hardcoded.
the second select box should show the data based on the selected data in first selectbox. this is the plunker.
https://plnkr.co/edit/r1S1e61H3RfH3uYGYTBP?p=preview
can anyone please help me.
$scope.data = [{
cities: [{
id: 1,
title: 'Mysore'
}, {
id: 2,
title: 'Bangalore'
}, {
id: 3,
title: 'Delhi'
}, {
id: 4,
title: 'Mumbai'
}],
maps: [{
id: 1,
title: 'Zoo',
city_id: 1
}, {
id: 2,
title: 'Palace',
city_id: 1
}, {
id: 3,
title: 'Beach',
city_id: 4
}]
}];
You can pass the selected city's id from 1st drop down as a filter to 2nd drop down like below
<select name="mapSelect" required="true" ng-options="map as map.title for map in data[0].maps | filter:{'city_id':citySelect.selectedOption.id}" ng-model="mapSelect.selectedOption"></select>

Create object using Underscore.js

What is the correct Underscore.js way to create a new object called items that consists of each of the item arrays. Where I could then make a POST of each item.name in one call?
var items = [];
item = [{
name: "item1",
desc: "this is a description of item 1",
qty: 1
},{
name: "item2",
desc: "this is a description of item 2",
qty: 5
},{
name: "item3",
desc: "this is a description of item 3",
qty: 3
}];
items.push(item);
If I understand the question correctly you want to convert an array of items to an object where each key is the name of the object
e.g.
{
item1: {
name: "item1",
desc: "this is a description of item 1",
qty: 1
},
item2: { ... },
item3: { ... },
}
If that's the case then you could use the object function that takes two parameters; the first being the list of property names and the second a list of the values:
var items = [{
name: "item1",
desc: "this is a description of item 1",
qty: 1
},{
name: "item2",
desc: "this is a description of item 2",
qty: 5
},{
name: "item3",
desc: "this is a description of item 3",
qty: 3
}
];
var itemsAsAnObject = _.object( _.pluck(items,'name'), items)

Resources