If I have the following array
someArray = [{id: 1, coordinates: {latitude: 1212, longitude: 13324}},{id: 2, coordinates: {latitude: 1314, longitude: 15151}}]
is there anyway to call someArray so I get just the array of the coordinates keys without having to make a new array? someArray.coordinates gives me undefined.
Expected output:
[{latitude: 1212, longitude: 13324}, {latitude: 1314, longitude: 15151}]
You can use Array#map (spec, MDN) for that:
someArray = someArray.map(function(entry) {
return entry.coordinates;
});
Array#map produces a new array from the entries you return from the iteration function you pass into it.
Live Example:
var someArray = [{id: 1, coordinates: {latitude: 1212, longitude: 13324 }}, {id: 2, coordinates: {latitude: 1314,longitude: 15151}}];
snippet.log("Before:");
snippet.log(JSON.stringify(someArray));
someArray = someArray.map(function(entry) {
return entry.coordinates;
});
snippet.log("After:");
snippet.log(JSON.stringify(someArray));
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
You can try with a map function:
someArray.map(function(current, index, array){ array[index] = current.coordinates});
This will, though, modify your original. You can get it as another array like:
var coordinates = [];
someArray.map(function(current){coordinates.push(current.coordinates)});
And yes, sorry, I forgot, the easiest is:
var coordinates = someArray.map(function(current) { return current.coordinates; });
Or you can write it yourself:
function getCoordinates(objectArray) {
var coordinates = [];
for (var i in objectArray) {
var current = objectArray[i];
coordinates.push(current.coordinates);
}
}
var onlyCoordinates = getCoordinates(someArray);
EDITED: Just use following snippet
var someArray = [{id: 1, coordinates: {latitude: 1212, longitude: 13324}},{id: 2, coordinates: {latitude: 1314, longitude: 15151}}];
var i = -1;
while(someArray[++i]){
someArray[i] = someArray[i].coordinates;
}
document.write("<pre>"+JSON.stringify(someArray,0,3)+"</pre>");
Related
We have a dictionary with cities, using a uniqe id as the key:
cities: {
'adfjlx9w': {
name: 'New York',
latitude: 4,
longitude: -7
},
'favkljsl9': {
name: 'Copenhagen',
latitude: 2,
longitude: -18
}
}
We need to convert our dictionary into Geojson in order to place the cities on a map, but cannot use the typical route below, as it is not an array of objects:
GeoJSON.parse(cities, {
Point: ['latitude', 'longitude']
});
What is the fastest and best way to do this?
If I understand correctly, you need to extract the latitude and longitude data for each value of the cities object to an array of latitude/longitude values of shape:
{ latitude, longitude }
One approach would be to use Object#values which returns an array of the values for cities, and, optional use Array#map to transform each object to a new object (with only the latitude, longitude values):
const cities = {
'adfjlx9w': {
name: 'New York',
latitude: 4,
longitude: -7
},
'favkljsl9': {
name: 'Copenhagen',
latitude: 2,
longitude: -18
}
}
const latlngArray = Object
// Extract value array from cities
.values(cities)
// Map each value to lat/lng only object
.map(item => ({ latitude : item.latitude, longitude : item.longitude }))
console.log(latlngArray);
/* Pass latlngArray to GeoJSON.parse
GeoJSON.parse(latlngArray, {
Point: ['latitude', 'longitude']
});
*/
Hope that helps!
Something like this should work.
const citiesArray = Object.values(cities);
GeoJSON.parse(citiesArray, {
Point: ['latitude', 'longitude']
});
According to the GeoJSON Specification, to include the id and name properties with latitude/longitude coordinates, you would need to parse the object as type FeatureCollection with a property features.
Each features array object should be of type Feature, with properties and geometry values. The properties value should contain the metadata, and the geometry value should be of type Point with coordinates property containing latitude/longitude.
const cities = {
'adfjlx9w': {
name: 'New York',
latitude: 4,
longitude: -7
},
'favkljsl9': {
name: 'Copenhagen',
latitude: 2,
longitude: -18
}
}
let citiesGeoJSON = Object.entries(cities)
.reduce((
_cities,
[cityID, cityData],
) => {
let city = {
"type": "Feature",
"properties": {
"id": cityID,
"name": cityData.name,
},
"geometry": {
"type": 'Point',
"coordinates": [
cityData.latitude,
cityData.longitude,
],
},
}
_cities.features.push(city)
return _cities
}, {
"type": "FeatureCollection",
"features": [],
})
There is a GeoJSON Linter online.
This question already has answers here:
How to remove all duplicates from an array of objects?
(77 answers)
Closed 4 years ago.
I am working with Geofire and Firebase on Angular 6 to store locations and unfortunately it's storing a lot of duplicates this is an example (console logging my variable currentHits):
0: {location: Array(2), distance: "48.84", url: "assets/imgs/fix.png"}
1: {location: Array(2), distance: "48.84", url: "assets/imgs/fix.png"}
2: {location: Array(2), distance: "48.84", url: "assets/imgs/fix.png"}
3: {location: Array(2), distance: "48.85", url: "assets/imgs/free.png"}
4: {location: Array(2), distance: "48.85", url: "assets/imgs/free.png"}
5: {location: Array(2), distance: "48.85", url: "assets/imgs/free.png"}
6: {location: Array(2), distance: "48.87", url: "assets/imgs/low.png"}
7: {location: Array(2), distance: "48.87", url: "assets/imgs/low.png"}
8: {location: Array(2), distance: "48.87", url: "assets/imgs/low.png"}
Location basically is an array of latitude and longitude used to calculate distance, in id 0, 1 and 2 its the same coordinates, and 3,4 and 5 are also the same, ...
This is what I want to get:
0: {location: Array(2), distance: "48.84", url: "assets/imgs/fix.png"}
1: {location: Array(2), distance: "48.85", url: "assets/imgs/free.png"}
2: {location: Array(2), distance: "48.87", url: "assets/imgs/low.png"}
(Optional) this is how It stores these locations:
...
hits = new BehaviorSubject([])
...
queryHits(...){
....
let hit = {
location: location,
distance: distance.toFixed(2),
url:img
}
let currentHits = this.hits.value
currentHits.push(hit)
this.hits.next(currentHits)
....
}
It's true that this question has probably already been asked and I have been digging through all the similar questions and found these functions:
1. RemoveDuplicates()
function removeDuplicates(arr){
let unique_array = []
for(let i = 0;i < arr.length; i++){
if(unique_array.indexOf(arr[i]) == -1){
unique_array.push(arr[i])
}
}
return unique_array
}
var newlist = removeDuplicates(list)
It didn't work I get the same list with duplicates.
2. arrUnique:
function arrUnique(arr) {
var cleaned = [];
arr.forEach(function(itm) {
var unique = true;
cleaned.forEach(function(itm2) {
if (_.isEqual(itm, itm2)) unique = false;
});
if (unique) cleaned.push(itm);
});
return cleaned;
}
var newlist= arrUnique(list);
Also, it didn't work..
3. onlyUnique
onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
var newlist = list.filter(onlyUnique)
Unfortunately it didn't work...
These are some of the answers given to similar problem to remove duplicates from an array and none of them worked. I don't understand why they won't work for my type of array, If anyone has an idea or knows why would be very helpful.
You could use a set to store and check for duplicate values.
const removeDuplicates = arr => {
let matches = new Set();
return arr.filter(elem => {
const {distance} = elem;
if(matches.has(distance)){
return false;
} else {
matches.add(distance);
return true;
}
})
}
Bear in mind that using this approach you may remove results where the distance is the same but the co-ordinates differ. If that causes an issue for you then you'd need to also check against the lat/lng pair.
Problem here is comparing Objects. Two objects are never equal unless both are referencing to same Object.
Example:
{} === {} // false
// Two objects are equal only if they are referencing to same object
var a = {};
a === a; // true
It is clear from your problem that you are facing the first case. Among the solutions you tested Solution 1 and Solution 3 are failing because of this reason as indexOf does === comparision.
But Solution 2 should have worked on your example as it does a deep comparision as explained here. https://lodash.com/docs#isEqual.
PS: It might be a simple typo i have observed in Solution 2 cleaned.,push(itm);, there is an extra comma. Hoping that is not the case I am moving ahead
So i guess the issue is inside your location array, if you can give the contents of location array we should be able to provide better solution. Or as others suggested you can filter based on a single key of the object like id or distance, instead of comparing the whole object
You can use following approach:
Idea:
You can create your own data structure and have a hashMap to save values.
Since you have location data, you can use longitude|latitude as your key name as it will be unique.
Then expose some functions say, add that will check if value exists, override else add.
Also create a property, say value that would return the list of locations.
Note: Above behavior can be achieved using Set as well. If you cannot use ES6 features, then this is one way that is extensible and easy.
function MyList() {
var locations = {};
this.add = function(value) {
var key = value.location.join('|');
locations[key] = value;
}
Object.defineProperty(this, 'value', {
get: function() {
return Object.keys(locations).map(function(key) {return locations[key] })
}
})
}
var locations = new MyList();
locations.add({location: [123.12, 456.23], name: 'test 1' });
locations.add({location: [123.16, 451.23], name: 'test 2' });
locations.add({location: [123.12, 456.23], name: 'test 1' });
locations.add({location: [100.12, 456.23], name: 'test 3' });
locations.add({location: [123.12, 456.23], name: 'test 1' });
locations.add({location: [123.12, 400.23], name: 'test 4' });
console.log(locations.value)
Typescript version for more readability:
interface ILocation {
location: Array<number>
[key: string]: any;
}
interface IList {
[key: string]: ILocation
}
class MyList {
private locations: IList = {};
public add(value: ILocation) {
const key: string = value.location.join('|');
this.locations[key] = value;
}
public get value(): Array<ILocation> {
return Object.keys(locations).map(function(key) {return locations[key] })
}
}
uniqWith https://lodash.com/docs/#uniqWith can be used to specify the method to compare by :
var arr = [ { location: [1, 2], distance: "48.84", url: "assets/imgs/fix.png" },
{ location: [1, 2], distance: "48.84", url: "assets/imgs/fix.png" },
{ location: [1, 2], distance: "48.84", url: "assets/imgs/fix.png" },
{ location: [3, 4], distance: "48.85", url: "assets/imgs/free.png"},
{ location: [3, 4], distance: "48.85", url: "assets/imgs/free.png"},
{ location: [3, 4], distance: "48.85", url: "assets/imgs/free.png"},
{ location: [5, 6], distance: "48.87", url: "assets/imgs/low.png" },
{ location: [5, 6], distance: "48.87", url: "assets/imgs/low.png" },
{ location: [5, 6], distance: "48.87", url: "assets/imgs/low.png" } ]
var result = _.uniqWith(arr, (a, b) => _.isEqual(a.location, b.location));
console.log( JSON.stringify({...result}).replace(/},/g, '},\n ') );
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.11/lodash.min.js"></script>
You could always check before you add the hits to make sure there are no repeats.
edit: You cannot compare objects unless they have the same reference object. So, you could compare objects by a unique ID
use rxjs filter()
this will return an array
// store history of objs for comparison
addedObjs = [];
this.hits.pipe(filter(obj => {
// check if id is an index of the previous objs
if (addObjs.indexOf(obj.id) === -1) {
this.addedObjs.push(obj.id)
return obj
});
here is the working stackblitz using some of your code
Perhaps you would want to use a library such as lodash which has a wide set of functions regarding all types of collections.
let newArr = _.uniqWith(myArr, _.isEqual);
uniqWith with the help of isEqual can get what you want.
Here is fiddle to that solution
I think it is your comparison that may not be working correctly. You can try this:
var uniqueHits = currentHits.reduce((acc, curr) => {
if (acc.length === 0) {
acc.push(curr);
} else if (!acc.some((item) =>
item.location[0] === curr.location[0]
&& item.location[1] === curr.location[1]
&& item.distance === curr.distance
&& item.url === curr.url)) {
acc.push(curr);
}
return accumulator;
}, []);;
I initialized the map like:
var map = new Map();
when i do console.log(map), i get:
testCreateBadAppointmentRequest:
{ name: 'testCreateBadAppointmentRequest',
time: 0.02926,
status: 'Passed' },
testAppointmentRequestAPI:
{ name: 'testAppointmentRequestAPI',
time: 0.051030000000000006,
status: 'Passed' },
I want to sort this map on the time attribute of the value.
How do i do that in nodejs?
Is there a ready sort function to do so?
You will need to convert the Map to an Array first then use the built-in sort and provide a callback:
const sorted = Array.from(map).sort(function(a, b) {
if (a.time < b.time) return -1;
if (a.time > b.time) return 1;
return 0;
});
Map order is determined by insertion order.
It should be noted that a Map which is a map of an object, especially a dictionary of dictionaries, will only map to the object's insertion order—which is random and not ordered.
Convert map to array with Array.from or using the spread operator on the map iterable. Then sort the array:
const map = new Map()
map.set('testCreateBadAppointmentRequest', { name: 'testCreateBadAppointmentRequest', time: 0.02926, status: 'Passed' });
map.set('testAppointmentRequestAPI', { name: 'testAppointmentRequestAPI', time: 0.051030000000000006, status: 'Passed' });
// convert map to array
console.log('array', [...map.entries()]);
const array = Array.from(map);
// sort (inverse sort to change your current sort)
array.sort((x, y) => y[1].time - x[1].time);
console.log('sorted', array);
// create new map with objects pairs in the desired order:
const timeSortedMap = new Map(array);
console.log('sorted map', [...timeSortedMap]);
You'll have to create a new Map object, since a Map object iterates its elements in insertion order.
const inputMap = new Map(
[
['testCreateBadAppointmentRequest',
{
name: 'testCreateBadAppointmentRequest',
time: 0.02926,
status: 'Passed'
}
],
['testAppointmentRequestAPI',
{
name: 'testAppointmentRequestAPI',
time: 0.051030000000000006,
status: 'Passed'
},
],
['another',
{
name: 'name',
time: 0.0001,
status: 'Passed'
},
]
]);
const sortedMap = new Map([...inputMap.entries()].sort((entryA, entryB) => entryB[1].time - entryA[1].time));
for (const value of sortedMap.values()) {
console.log(value)
}
what i need is to transform this Array
[
0: {id: 1},
1: {id: 2}
]
to
[
1: {id: 1},
2: {id: 2}
]
Just switching keys I suppose, Im trying to use .map but all i can change are the values not the keys
I think you can solve it easily enough with reduce
It starts with an empty array, and during each iteration 1 item is added, at the index of the items id field.
This will give you an array with a length that might not be what you have expected, so I would suggest to look a bit deeper into your requirements.
var arr = [
{id: 1},
{id: 2}
];
var result = arr.reduce( (container, item) => {
container[item.id] = item;
return container;
}, [] );
console.log( result[1] );
console.log( result[2] );
console.log( result );
You could always make it an object with the correct keys, with pretty much the same syntax
var arr = [
{id: 1},
{id: 2}
];
var result = arr.reduce( (container, item) => {
container[item.id] = item;
return container;
}, {} );
console.log( result[1] );
console.log( result[2] );
console.log( result );
And important thing to note, is that with the array, you have an undefined value at position 0, the object version doesn't have it. It would make more sense to use the object version, and not the array. As mentioned in the comments, it's not a very good idea to have gaps in your array.
I have an array of objects that look something like this;
[
{Number: 5002000, Origin: 123456, Count: 128},
{Number: 5002300, Origin: 900231, Count: 52},
{Number: 5002022, Origin: 534323, Count: 269}
]
Now I'm trying to multiply the "Count" value with a value from a designated price pool.
Which looks something like this;
[
{Prefix: 50023, Price: 20},
{Prefix: 50020, Price: 10},
{Prefix: 5002, Price: 60},
]
Currently there's an horrendous for loop with if-statements.
for (var key in sData) {
if (sData[key].Origin.startsWith('50023')) {
sData[key].sum = (sData[key].Count * 20);
}
else if (sData[key].Origin.startsWith('50020')) {
sData[key].sum = (sData[key].Count * 10);
}
// continues...
}
startsWith is a function that simply checks if the value starts with the (value).
Is there already a function in JS to map two arrays of objects? (I'm also having issues with the logic since the "Prefix" value basically has to go from the top down as not to land on the default "5002"-prefix.)
You should use nested loops in this situation. Also switch to Array.forEach method.
sData.forEach(function(item_, key) {
prices.forEach(function(item) {
if (sData[key].Origin.startsWith(item.Prefix)) {
sData[key].sum = (sData[key].Count * item.Price);
}
});
})
Assuming that second array can be transformed into the hash:
var tiers = {
50023: {Prefix: 50023, Price: 20},
50020: {Prefix: 50020, Price: 10},
5002: {Prefix: 5002, Price: 60},
}
You may make it look like this:
for (var key in sData) {
var key = String(sData[key])
var keyIndex = key.slice(5)
if (tiers.hasOwnProperty(keyIndex)) {
var price = tiers[keyIndex].Price
sData[key].sum = (sData[key].Count * price)
} else {
// Fallback solution
}
}
Going further you may even think of some recursive solution for fallback.