duplicates into a JS Object property becomes 'true' after iteration - javascript

New to JS object and I just need someone to explain to me, why the object property becomes true when adding a duplicate value from an array. Below is my example using forEach()or reduce() method in removing duplicates. The undefined's makes sense to me. Only the true's, that I don't understand yet.
const names = ['James','John', 'James','Bob','John','Steve', 'James']
let unique = {};
names.forEach(el => {
console.log(unique[el])
if (!unique[el]) unique[el] = true;
});
console.log result inside forEach():
/*
unique['James'] = undefined
unique['John'] = undefined
unique['James'] = true
unique['Bob'] = undefined
unique['James'] = true
unique['Steve'] = undefined
unique['James'] = true
*/
console.log(Object.keys(unique)); // [ 'James', 'John', 'Bob', 'Steve' ]
Same in using reduce()method:
Object.keys(names.reduce((a,v) => {
if(!a[v]) a[v] = true;
return a;
},{}))
// [ 'James', 'John', 'Bob', 'Steve' ]

firstly it undefined, it looks like
first circle
console.log(unique["James"]) // undefined
if (!unique["James"]) { unique["James"] = true; } // it creates { James: true }
second circle ("James" already exists)
console.log(unique["James"]) // true
// if statement doesn't work
Here are simple examples of solving
const names = ['James','John', 'James','Bob','John','Steve', 'James'];
// example for array
let uniqueArray = [];
for(i=0; i < names.length; i++) {
if(uniqueArray.indexOf(names[i]) === -1) {
uniqueArray.push(names[i]);
}
}
console.log(uniqueArray)
// example for object
const uniqueObject = {};
names.forEach(el => uniqueObject[el] = true);
console.log(uniqueObject)
uniqueArray = Object.keys(uniqueObject)
console.log(uniqueArray)

Related

Js how to set an item at a nested index in an empty array [duplicate]

I hope someone can help me with this Javascript.
I have an Object called "Settings" and I would like to write a function that adds new settings to that object.
The new setting's name and value are provided as strings. The string giving the setting's name is then split by the underscores into an array. The new setting should get added to the existing "Settings" object by creating new nested objects with the names given by each part of the array, except the last part which should be a string giving the setting's value. I should then be able to refer to the setting and e.g. alert its value. I can do this in a static way like this...
var Settings = {};
var newSettingName = "Modules_Video_Plugin";
var newSettingValue = "JWPlayer";
var newSettingNameArray = newSettingName.split("_");
Settings[newSettingNameArray[0]] = {};
Settings[newSettingNameArray[0]][newSettingNameArray[1]] = {};
Settings[newSettingNameArray[0]][newSettingNameArray[1]][newSettingNameArray[2]] = newSettingValue;
alert(Settings.Modules.Mediaplayers.Video.Plugin);
... the part that creates the nested objects is doing this ...
Settings["Modules"] = {};
Settings["Modules"]["Video"] = {};
Settings["Modules"]["Video"]["Plugin"] = "JWPlayer";
However, as the number of parts that make up the setting name can vary, e.g. a newSettingName could be "Modules_Floorplan_Image_Src", I'd like to do this dynamically using a function such as...
createSetting (newSettingNameArray, newSettingValue);
function createSetting(setting, value) {
// code to create new setting goes here
}
Can anyone help me work out how to do this dynamically?
I presume there has to be a for...loop in there to itterate through the array, but I haven't been able to work out a way to create the nested objects.
If you've got this far thanks very much for taking the time to read even if you can't help.
Put in a function, short and fast (no recursion).
var createNestedObject = function( base, names ) {
for( var i = 0; i < names.length; i++ ) {
base = base[ names[i] ] = base[ names[i] ] || {};
}
};
// Usage:
createNestedObject( window, ["shapes", "triangle", "points"] );
// Now window.shapes.triangle.points is an empty object, ready to be used.
It skips already existing parts of the hierarchy. Useful if you are not sure whether the hierarchy was already created.
Or:
A fancier version where you can directly assign the value to the last object in the hierarchy, and you can chain function calls because it returns the last object.
// Function: createNestedObject( base, names[, value] )
// base: the object on which to create the hierarchy
// names: an array of strings contaning the names of the objects
// value (optional): if given, will be the last object in the hierarchy
// Returns: the last object in the hierarchy
var createNestedObject = function( base, names, value ) {
// If a value is given, remove the last name and keep it for later:
var lastName = arguments.length === 3 ? names.pop() : false;
// Walk the hierarchy, creating new objects where needed.
// If the lastName was removed, then the last object is not set yet:
for( var i = 0; i < names.length; i++ ) {
base = base[ names[i] ] = base[ names[i] ] || {};
}
// If a value was given, set it to the last name:
if( lastName ) base = base[ lastName ] = value;
// Return the last object in the hierarchy:
return base;
};
// Usages:
createNestedObject( window, ["shapes", "circle"] );
// Now window.shapes.circle is an empty object, ready to be used.
var obj = {}; // Works with any object other that window too
createNestedObject( obj, ["shapes", "rectangle", "width"], 300 );
// Now we have: obj.shapes.rectangle.width === 300
createNestedObject( obj, "shapes.rectangle.height".split('.'), 400 );
// Now we have: obj.shapes.rectangle.height === 400
Note: if your hierarchy needs to be built from values other that standard objects (ie. not {}), see also TimDog's answer below.
Edit: uses regular loops instead of for...in loops. It's safer in cases where a library modifies the Array prototype.
function assign(obj, keyPath, value) {
lastKeyIndex = keyPath.length-1;
for (var i = 0; i < lastKeyIndex; ++ i) {
key = keyPath[i];
if (!(key in obj)){
obj[key] = {}
}
obj = obj[key];
}
obj[keyPath[lastKeyIndex]] = value;
}
Usage:
var settings = {};
assign(settings, ['Modules', 'Video', 'Plugin'], 'JWPlayer');
My ES2015 solution. Keeps existing values.
const set = (obj, path, val) => {
const keys = path.split('.');
const lastKey = keys.pop();
const lastObj = keys.reduce((obj, key) =>
obj[key] = obj[key] || {},
obj);
lastObj[lastKey] = val;
};
Example:
const obj = {'a': {'prop': {'that': 'exists'}}};
set(obj, 'a.very.deep.prop', 'value');
console.log(JSON.stringify(obj));
// {"a":{"prop":{"that":"exists"},"very":{"deep":{"prop":"value"}}}}
Using ES6 is shorten. Set your path into an array.
first, you have to reverse the array, to start filling the object.
let obj = ['a','b','c'] // {a:{b:{c:{}}}
obj.reverse();
const nestedObject = obj.reduce((prev, current) => (
{[current]:{...prev}}
), {});
Another recursive solution:
var nest = function(obj, keys, v) {
if (keys.length === 1) {
obj[keys[0]] = v;
} else {
var key = keys.shift();
obj[key] = nest(typeof obj[key] === 'undefined' ? {} : obj[key], keys, v);
}
return obj;
};
Example usage:
var dog = {bark: {sound: 'bark!'}};
nest(dog, ['bark', 'loudness'], 66);
nest(dog, ['woff', 'sound'], 'woff!');
console.log(dog); // {bark: {loudness: 66, sound: "bark!"}, woff: {sound: "woff!"}}
I love this ES6 immutable way to set certain value on nested field:
const setValueToField = (fields, value) => {
const reducer = (acc, item, index, arr) => ({ [item]: index + 1 < arr.length ? acc : value });
return fields.reduceRight(reducer, {});
};
And then use it with creating your target object.
const targetObject = setValueToField(['one', 'two', 'three'], 'nice');
console.log(targetObject); // Output: { one: { two: { three: 'nice' } } }
Lodash has a _.set method to achieve this
let obj = {}
_.set(obj, ['a', 'b', 'c', 'd'], 'e')
or
_.set(obj, 'a.b.c.d', 'e')
// which generate the following object
{
"a": {
"b": {
"c": {
"d": "e"
}
}
}
}
Here is a simple tweak to jlgrall's answer that allows setting distinct values on each element in the nested hierarchy:
var createNestedObject = function( base, names, values ) {
for( var i in names ) base = base[ names[i] ] = base[ names[i] ] || (values[i] || {});
};
Hope it helps.
Here is a functional solution to dynamically create nested objects.
const nest = (path, obj) => {
const reversedPath = path.split('.').reverse();
const iter = ([head, ...tail], obj) => {
if (!head) {
return obj;
}
const newObj = {[head]: {...obj}};
return iter(tail, newObj);
}
return iter(reversedPath, obj);
}
Example:
const data = {prop: 'someData'};
const path = 'a.deep.path';
const result = nest(path, data);
console.log(JSON.stringify(result));
// {"a":{"deep":{"path":{"prop":"someData"}}}}
Inspired by ImmutableJS setIn method which will never mutate the original. This works with mixed array and object nested values.
function setIn(obj = {}, [prop, ...rest], value) {
const newObj = Array.isArray(obj) ? [...obj] : {...obj};
newObj[prop] = rest.length ? setIn(obj[prop], rest, value) : value;
return newObj;
}
var obj = {
a: {
b: {
c: [
{d: 5}
]
}
}
};
const newObj = setIn(obj, ["a", "b", "c", 0, "x"], "new");
//obj === {a: {b: {c: [{d: 5}]}}}
//newObj === {a: {b: {c: [{d: 5, x: "new"}]}}}
Appreciate that this question is mega old! But after coming across a need to do something like this in node, I made a module and published it to npm.
Nestob
var nestob = require('nestob');
//Create a new nestable object - instead of the standard js object ({})
var newNested = new nestob.Nestable();
//Set nested object properties without having to create the objects first!
newNested.setNested('biscuits.oblong.marmaduke', 'cheese');
newNested.setNested(['orange', 'tartan', 'pipedream'], { poppers: 'astray', numbers: [123,456,789]});
console.log(newNested, newNested.orange.tartan.pipedream);
//{ biscuits: { oblong: { marmaduke: 'cheese' } },
orange: { tartan: { pipedream: [Object] } } } { poppers: 'astray', numbers: [ 123, 456, 789 ] }
//Get nested object properties without having to worry about whether the objects exist
//Pass in a default value to be returned if desired
console.log(newNested.getNested('generic.yoghurt.asguard', 'autodrome'));
//autodrome
//You can also pass in an array containing the object keys
console.log(newNested.getNested(['chosp', 'umbridge', 'dollar'], 'symbols'));
//symbols
//You can also use nestob to modify objects not created using nestob
var normalObj = {};
nestob.setNested(normalObj, 'running.out.of', 'words');
console.log(normalObj);
//{ running: { out: { of: 'words' } } }
console.log(nestob.getNested(normalObj, 'random.things', 'indigo'));
//indigo
console.log(nestob.getNested(normalObj, 'improbable.apricots'));
//false
Inside your loop you can use lodash.set and will create the path for you:
...
const set = require('lodash.set');
const p = {};
const [type, lang, name] = f.split('.');
set(p, [lang, type, name], '');
console.log(p);
// { lang: { 'type': { 'name': '' }}}
try using recursive function:
function createSetting(setting, value, index) {
if (typeof index !== 'number') {
index = 0;
}
if (index+1 == setting.length ) {
settings[setting[index]] = value;
}
else {
settings[setting[index]] = {};
createSetting(setting, value, ++index);
}
}
I think, this is shorter:
Settings = {};
newSettingName = "Modules_Floorplan_Image_Src";
newSettingValue = "JWPlayer";
newSettingNameArray = newSettingName.split("_");
a = Settings;
for (var i = 0 in newSettingNameArray) {
var x = newSettingNameArray[i];
a[x] = i == newSettingNameArray.length-1 ? newSettingValue : {};
a = a[x];
}
I found #jlgrall's answer was great but after simplifying it, it didn't work in Chrome. Here's my fixed should anyone want a lite version:
var callback = 'fn.item1.item2.callbackfunction',
cb = callback.split('.'),
baseObj = window;
function createNestedObject(base, items){
$.each(items, function(i, v){
base = base[v] = (base[v] || {});
});
}
callbackFunction = createNestedObject(baseObj, cb);
console.log(callbackFunction);
I hope this is useful and relevant. Sorry, I've just smashed this example out...
You can define your own Object methods; also I'm using underscore for brevity:
var _ = require('underscore');
// a fast get method for object, by specifying an address with depth
Object.prototype.pick = function(addr) {
if (!_.isArray(addr)) return this[addr]; // if isn't array, just get normally
var tmpo = this;
while (i = addr.shift())
tmpo = tmpo[i];
return tmpo;
};
// a fast set method for object, put value at obj[addr]
Object.prototype.put = function(addr, val) {
if (!_.isArray(addr)) this[addr] = val; // if isn't array, just set normally
this.pick(_.initial(addr))[_.last(addr)] = val;
};
Sample usage:
var obj = {
'foo': {
'bar': 0 }}
obj.pick('foo'); // returns { bar: 0 }
obj.pick(['foo','bar']); // returns 0
obj.put(['foo', 'bar'], -1) // obj becomes {'foo': {'bar': -1}}
A snippet for those who need to create a nested objects with support of array keys to set a value to the end of path. Path is the string like: modal.product.action.review.2.write.survey.data. Based on jlgrall version.
var updateStateQuery = function(state, path, value) {
var names = path.split('.');
for (var i = 0, len = names.length; i < len; i++) {
if (i == (len - 1)) {
state = state[names[i]] = state[names[i]] || value;
}
else if (parseInt(names[i+1]) >= 0) {
state = state[names[i]] = state[names[i]] || [];
}
else {
state = state[names[i]] = state[names[i]] || {};
}
}
};
Set Nested Data:
function setNestedData(root, path, value) {
var paths = path.split('.');
var last_index = paths.length - 1;
paths.forEach(function(key, index) {
if (!(key in root)) root[key] = {};
if (index==last_index) root[key] = value;
root = root[key];
});
return root;
}
var obj = {'existing': 'value'};
setNestedData(obj, 'animal.fish.pet', 'derp');
setNestedData(obj, 'animal.cat.pet', 'musubi');
console.log(JSON.stringify(obj));
// {"existing":"value","animal":{"fish":{"pet":"derp"},"cat":{"pet":"musubi"}}}
Get Nested Data:
function getNestedData(obj, path) {
var index = function(obj, i) { return obj && obj[i]; };
return path.split('.').reduce(index, obj);
}
getNestedData(obj, 'animal.cat.pet')
// "musubi"
getNestedData(obj, 'animal.dog.pet')
// undefined
Try this: https://github.com/silkyland/object-to-formdata
var obj2fd = require('obj2fd/es5').default
var fd = obj2fd({
a:1,
b:[
{c: 3},
{d: 4}
]
})
Result :
fd = [
a => 1,
b => [
c => 3,
d => 4
]
]
Here is a decomposition to several useful functions, that each preserve existing data. Does not handle arrays.
setDeep: Answers question. Non-destructive to other data in the object.
setDefaultDeep: Same, but only sets if not already set.
setDefault: Sets a key if not already set. Same as Python's setdefault.
setStructure: Helper function that builds the path.
// Create a nested structure of objects along path within obj. Only overwrites the final value.
let setDeep = (obj, path, value) =>
setStructure(obj, path.slice(0, -1))[path[path.length - 1]] = value
// Create a nested structure of objects along path within obj. Does not overwrite any value.
let setDefaultDeep = (obj, path, value) =>
setDefault(setStructure(obj, path.slice(0, -1)), path[path.length - 1], value)
// Set obj[key] to value if key is not in object, and return obj[key]
let setDefault = (obj, key, value) =>
obj[key] = key in obj ? obj[key] : value;
// Create a nested structure of objects along path within obj. Does not overwrite any value.
let setStructure = (obj, path) =>
path.reduce((obj, segment) => setDefault(obj, segment, {}), obj);
// EXAMPLES
let temp = {};
// returns the set value, similar to assignment
console.log('temp.a.b.c.d:',
setDeep(temp, ['a', 'b', 'c', 'd'], 'one'))
// not destructive to 'one'
setDeep(temp, ['a', 'b', 'z'], 'two')
// does not overwrite, returns previously set value
console.log('temp.a.b.z: ',
setDefaultDeep(temp, ['a', 'b', 'z'], 'unused'))
// creates new, returns current value
console.log('temp["a.1"]: ',
setDefault(temp, 'a.1', 'three'))
// can also be used as a getter
console.log("temp.x.y.z: ",
setStructure(temp, ['x', 'y', 'z']))
console.log("final object:", temp)
I'm not sure why anyone would want string paths:
They are ambiguous for keys with periods
You have to build the strings in the first place
Since I started with something from this page, I wanted to contribute back
Other examples overwrote the final node even if it was set, and that wasn't what I wanted.
Also, if returnObj is set to true, it returns the base object. By default, falsy, it returns the deepest node.
function param(obj, path, value, returnObj) {
if (typeof path == 'string') path = path.split(".");
var child = obj;
path.forEach((key, i) => {
if (!(key in child)) {
child[key] = (i < path.length-1) ? {} : value || {};
}
child = child[key];
});
return returnObj ? obj : child;
}
var x = {};
var xOut = param(x, "y.z", "setting")
console.log(xOut);
xOut = param(x, "y.z", "overwrite") // won't set
console.log(xOut);
xOut = param(x, "y.a", "setting2")
console.log(xOut);
xOut = param(x, "y.a", "setting2", true) // get object rather than deepest node.
console.log(xOut);
You can also do something where numeric keys are placed in arrays (if they don't already exist). Note that numeric keys won't convert to arrays for the first element of the path, since that's set by the type of your base-object.
function isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
function param(obj, path, value, returnObj) {
if (typeof path == 'string') path = path.split(".");
var child = obj;
path.forEach((key, i) => {
var nextKey = path[i+1];
if (!(key in child)) {
child[key] = (nextKey == undefined && value != undefined
? value
: isNumber(nextKey)
? []
: {});
}
child = child[key];
});
return returnObj ? obj : child;
}
var x = {};
var xOut = param(x, "y.z", "setting")
console.log(xOut);
xOut = param(x, "y.z", "overwrite") // won't set
console.log(xOut);
xOut = param(x, "y.a", "setting2")
console.log(xOut);
xOut = param(x, "y.a", "setting2", true) // get object rather than deepest node.
xOut = param(x, "1.0.2.a", "setting")
xOut = param(x, "1.0.1.a", "try to override") // won't set
xOut = param(x, "1.0.5.a", "new-setting", true) // get object rather than deepest node.
console.log(xOut);
Naturally, when the numeric keys are greater than 0, you might see some undefined gaps.
Practical uses of this might be
function AddNote(book, page, line) {
// assume a global global notes collection
var myNotes = param(allNotes, [book, page, line], []);
myNotes.push('This was a great twist!')
return myNotes;
}
var allNotes = {}
var youthfulHopes = AddNote('A Game of Thrones', 4, 2, "I'm already hooked, at least I won't have to wait long for the books to come out!");
console.log(allNotes)
// {"A Game of Thrones": [undefined, undefined, undefined, undefined, [undefined, undefined, ["I'm already hooked, at least I won't have to wait long for the books to come out!"]]]}
console.log(youthfulHopes)
// ["I'm already hooked, at least I won't have to wait long for the books to come out!"]
function initPath(obj, path) {
path.split('.').reduce((o, key) => (
Object.assign(o, {[key]: Object(o[key])}),
o[key]
), obj);
return obj;
}
Usage
const obj = { a: { b: 'value1' } };
initPath(obj, 'a.c.d').a.c.d='value2';
/*
{
"a": {
"b": "value1",
"c": {
"d": "value2"
}
}
}
*/
simple answer. on es6, im using this
const assign = (obj, path, value) => {
let keyPath = path.split('.')
let lastKeyIndex = keyPath.length - 1
for (let i = 0; i < lastKeyIndex; ++i) {
let key = keyPath[i]
if (!(key in obj)) {
obj[key] = {}
}
obj = obj[key]
}
obj[keyPath[lastKeyIndex]] = value
}
example json
const obj = {
b: 'hello'
}
you can add new key
assign(obj, 'c.d.e', 'this value')
and you get like bellow
console.log(obj)
//response example
obj = {
b: 'hello',
c: {
d: {
e: 'this value'
}
}
}
function createObj(keys, value) {
let obj = {}
let schema = obj
keys = keys.split('.')
for (let i = 0; i < keys.length - 1; i++) {
schema[keys[i]] = {}
schema = schema[keys[i]]
}
schema[keys.pop()] = value
return obj
}
let keys = 'value1.value2.value3'
let value = 'Hello'
let obj = createObj(keys, value)
Eval is probably overkill but the result is simple to visualize, with no nested loops or recursion.
function buildDir(obj, path){
var paths = path.split('_');
var final = paths.pop();
for (let i = 1; i <= paths.length; i++) {
var key = "obj['" + paths.slice(0, i).join("']['") + "']"
console.log(key)
eval(`${key} = {}`)
}
eval(`${key} = '${final}'`)
return obj
}
var newSettingName = "Modules_Video_Plugin_JWPlayer";
var Settings = buildDir( {}, newSettingName );
Basically you are progressively writing a string "obj['one']= {}", "obj['one']['two']"= {} and evaling it;

Cleaning the json object by removing duplicates and null and merging them into a single record

Cleaning the JSON object by removing duplicates and null and merging them into a single record
The json array looks like this:
var result =
[
{"id":"10035","occupation":null,"state":"FL"},
{"id":"10035","occupation":"doctor","state":null},
{"id":"10035","occupation":null,"state":null},
]
I want to merge records into one neglecting all the null fields and make it as a single record.Below is my expected output:
[
{"id":"10035","occupation":"doctor","state":"FL"}
]
You could do it with this ES6 script:
let data = [
{"id":"10035","occupation":null,"state":"FL"},
{"id":"10035","occupation":"doctor","state":null},
{"id":"10035","occupation":null,"state":null},
];
let result = Object.values(data.reduce ( (acc, {id, occupation, state}) => {
acc[id] = Object.assign({ id }, acc[id],
occupation && { occupation },
state && { state });
return acc;
}, {}));
console.log(result);
It will still produce multiple records if you have different id values in your input. When there are more than one non-null values for the other properties, but for the same id, then only the last one will survive.
When you're without support for Object.values
Use this definition of it:
Object.values = Object.values || (o => Object.keys(o).map(k => o[k]));
var final = {};
for (var i in result) {
for (var k in result[i]) {
if (result[i][k] && final[k] !== result[i][k]) {
final[k] = result[i][k];
}
}
}
console.log(final); // outputs: {id: "10035", state: "FL", occupation: "doctor"}
Here's a simple to understand example, which works for objects with any number of properties.
let data = [
{"id":"10035","occupation":null,"state":"FL"},
{"id":"10035","occupation":"doctor","state":null},
{"id":"10035","occupation":null,"state":null},
];
let result = data[0];
data.forEach(obj=> { // iterate through all objects in array
for(key in obj) // iterate through all properties of objects
if(obj[key]) result[key] = obj[key]; // if not null, assign to final result
});
console.log(result);
Here is a way to do it in O(n) time:
const mergeObjects = (data) => {
const dataWithoutDuplicates = {};
// first pass will get rid of dupes
let user;
for(let i = 0; i < data.length; data++) {
user = data[i];
if(!dataWithoutDuplicates[user.id]) {
dataWithoutDuplicates[user.id] = user
} else {
Object.keys(dataWithoutDuplicates[user.id]).forEach(key => {
if(dataWithoutDuplicates[user.id][key] === null && user[key]) {
dataWithoutDuplicates[user.id][key] = user[key]
}
})
}
return Object.values(dataWithoutDuplicates)
}

Get the first and last item in an Array - JS

I am trying to get the first and last item in array and display them in an object.
What i did is that I use the first and last function and then assign the first item as the key and the last item as the value.
var myArray = ['Rodel', 'Mike', 'Ronnie', 'Betus'];
function firstAndLast(array) {
var firstItem = myArray.first;
var lastItem = myArray.last;
var objOutput = {
firstItem : lastItem
};
}
var display = transformFirstAndLast(myArray);
console.log(display);
however this one gets me into trouble. It says undefined. Any idea why is that?
I've modified your code :
var myArray = ['Rodel', 'Mike', 'Ronnie', 'Betus'];
function firstAndLast(array) {
var firstItem = myArray[0];
var lastItem = myArray[myArray.length-1];
var objOutput = {
first : firstItem,
last : lastItem
};
return objOutput;
}
var display = firstAndLast(myArray);
console.log(display);
UPDATE: New Modification
var myArray = ['Rodel', 'Mike', 'Ronnie', 'Betus'];
function firstAndLast(array) {
var firstItem = myArray[0];
var lastItem = myArray[myArray.length-1];
var objOutput = {};
objOutput[firstItem]=lastItem
return objOutput;
}
var display = firstAndLast(myArray);
console.log(display);
With ES6 and destructuring:
const myArray = ['Rodel', 'Mike', 'Ronnie', 'Betus'];
const { 0: first, length, [length -1]: last } = myArray //getting first and last el from array
const obj = { first, last }
console.log(obj) // { first: "Rodel", last: "Betus" }
ES6
var objOutput = { [myArray[0]]: [...myArray].pop() }
var myArray = ['Rodel', 'Mike', 'Ronnie', 'Betus'];
var objOutput = { [myArray[0]]: [...myArray].pop() }
console.log(objOutput);
As of 2021, you can use Array.prototype.at()
let colors = ['red', 'green', 'blue']
let first = colors.at(0) // red
let last = colors.at(-1) // blue
To read more about Array.prototype.at()
I prefer using a simple array filter:
const myArray = ['one', 2, 3, 4, 5];
const filterFirstLast = (e, i, a) => i === 0 || i === a.length - 1;
const [ first, last ] = myArray.filter(filterFirstLast);
console.log(first, last); // outputs: 'one', 5
// dealing with insufficient input is another topic (input array length < 2)
console.log(['one'].filter(filterFirstLast )); // outputs: ['one']
console.log([].filter(filterFirstLast )); // outputs: []
transforming to something else is as easy
const obj = { [first]: last }; // will be: { one: 5 }, but never { one: "one" }
// OR, if you allow ['one'] to apply for both, if last is nullish (e.g. undefined, null, ""):
const obj = { [first]: last || first }; // will be: { one: 5 }, can be { one: "one" }
Another variation of roli answer
function firstAndLast(array) {
return { [[...array].shift()]: [...array].pop() };
}
To make your first and last properties work as you want, define them as properties with "getters" on the array prototype.
(You also have an inconsistency between firstAndLastArray and transformFirstAndLast, which needed to be fixed.)
Returning {firstName: lastName} will not do what you presumably want, since it will yield the key firstName. To use the actual first name as the key, use computed property names ({[firstName]: lastName}).
Object.defineProperties(Array.prototype, {
first: { get() { return this[0]; }},
last: { get() { return this[this.length - 1]; }}
});
var myArray = ['Rodel', 'Mike', 'Ronnie', 'Betus'];
function firstAndLast(array) {
var firstItem = myArray.first;
var lastItem = myArray.last;
return {[firstItem]: lastItem};
}
var display = firstAndLast(myArray);
console.log(display);
Or, much more simply, just
function firstAndLast(array) {
return {[array[0]]: array[array.length - 1]};
}
If you don't want to, or cannot, use computed property names, then
function firstAndLast(array) {
var result = {};
result[array[0]] = array[array.length - 1];
return result;
}
Do like this :-
var myArray = ['Rodel', 'Mike', 'Ronnie', 'Betus'];
function firstAndLast(myArr) {
var firstItem = myArr[0];
var lastItem = myArr[myArr.length - 1];
var objOutput = {};
objOutput[firstItem] = lastItem;
return objOutput;
}
var display = firstAndLast(myArray);
console.log(display);
well, I have another idea... for ex.:
const all = ['food', 'clean', 'cat', 'shower', 'work out']
console.log(`you have ${all.length} all!`)
console.log(`Todo: ${all[0]}`)
console.log(`Todo: ${all[all.length - 1]}`)
try this to find out first and last value of an array
var array = ['Rodel', 'Mike', 'Ronnie', 'Betus'];
let {0 : a ,[array.length - 1] : b} = array;
var objOutput = {
first : a,
last:b
};
console.log(objOutput)
In Array :- Check first and last element in array are same or not..
function hasSame(arr1, arr2) {
for(i=0; i <arr1.length ; i++) {
for(j=0; j <arr2.length ; j++) {
if(arr1[0] === arr2[0] && arr1[2] === arr2[2]) {
return true
} else
return false;
}
}
}
console.log(hasSame(
["white bread", "lettuce", "toast"],
["white bread", "tomato", "toast"]
))

array indexOf with objects?

I know we can match array values with indexOf in JavaScript. If it matches it wont return -1.
var test = [
1, 2, 3
]
// Returns 2
test.indexOf(3);
Is there a way to match objects? For example?
var test = [
{
name: 'Josh'
}
]
// Would ideally return 0, but of course it's -1.
test.indexOf({ name: 'Josh' });
Since the two objects are distinct (though perhaps equivalent), you can't use indexOf.
You can use findIndex with a callback, and handle the matching based on the properties you want. For instance, to match on all enumerable props:
var target = {name: 'Josh'};
var targetKeys = Object.keys(target);
var index = test.findIndex(function(entry) {
var keys = Object.keys(entry);
return keys.length == targetKeys.length && keys.every(function(key) {
return target.hasOwnProperty(key) && entry[key] === target[key];
});
});
Example:
var test = [
{
name: 'Josh'
}
];
var target = {name: 'Josh'};
var targetKeys = Object.keys(target);
var index = test.findIndex(function(entry) {
var keys = Object.keys(entry);
return keys.length == targetKeys.length && keys.every(function(key) {
return target.hasOwnProperty(key) && entry[key] === target[key];
});
});
console.log(index);
Note that findIndex was added in ES2015, but is fully polyfillable.
Nope, you can't and the explanation is simple. Despite you use the same object literal, two different objects are created. So test would have another reference for the mentioned object if you compare it with the reference you are looking for in indexOf.
This is kind of custom indexOf function. The code just iterates through the items in the object's array and finds the name property of each and then tests for the name you're looking for. Testing for 'Josh' returns 0 and testing for 'Kate' returns 1. Testing for 'Jim' returns -1.
var test = [
{
name: 'Josh'
},
{
name: 'Kate'
}
]
myIndexOf('Kate')
function myIndexOf(name) {
testName = name;
for (var i = 0; i < test.length; i++) {
if(test[i].hasOwnProperty('name')) {
if(test[i].name === testName) {
console.log('name: ' + test[i].name + ' index: ' + i);
return i;
}
}
}
return -1;
}
You can loop on array and then look for what you want
var test = [{ name: 'Josh' }]
const Myname = test.map((item) => { return item.name; }).indexOf("Josh")

How can I get the index of an object by its property in JavaScript?

For example, I have:
var Data = [
{ id_list: 1, name: 'Nick', token: '312312' },
{ id_list: 2, name: 'John', token: '123123' },
]
Then, I want to sort/reverse this object by name, for example. And then I want to get something like this:
var Data = [
{ id_list: 2, name: 'John', token: '123123' },
{ id_list: 1, name: 'Nick', token: '312312' },
]
And now I want to know the index of the object with property name='John' to get the value of the property token.
How do I solve the problem?
Since the sort part is already answered. I'm just going to propose another elegant way to get the indexOf of a property in your array
Your example is:
var Data = [
{id_list:1, name:'Nick', token:'312312'},
{id_list:2, name:'John', token:'123123'}
]
You can do:
var index = Data.map(function(e) { return e.name; }).indexOf('Nick');
var Data = [{
id_list: 1,
name: 'Nick',
token: '312312'
},
{
id_list: 2,
name: 'John',
token: '123123'
}
]
var index = Data.map(function(e) {
return e.name;
}).indexOf('Nick');
console.log(index)
Array.prototype.map is not available on Internet Explorer 7 or Internet Explorer 8. ES5 Compatibility
And here it is with ES6 and arrow syntax, which is even simpler:
const index = Data.map(e => e.name).indexOf('Nick');
If you're fine with using ES6, arrays now have the findIndex function. Which means you can do something like this:
const index = Data.findIndex(item => item.name === 'John');
As the other answers suggest, looping through the array is probably the best way. But I would put it in its own function, and make it a little more abstract:
function findWithAttr(array, attr, value) {
for(var i = 0; i < array.length; i += 1) {
if(array[i][attr] === value) {
return i;
}
}
return -1;
}
var Data = [
{id_list: 2, name: 'John', token: '123123'},
{id_list: 1, name: 'Nick', token: '312312'}
];
With this, not only can you find which one contains 'John', but you can find which contains the token '312312':
findWithAttr(Data, 'name', 'John'); // returns 0
findWithAttr(Data, 'token', '312312'); // returns 1
findWithAttr(Data, 'id_list', '10'); // returns -1
The function returns -1 when not found, so it follows the same construct as Array.prototype.indexOf().
If you're having issues with Internet Explorer, you could use the map() function which is supported from 9.0 onward:
var index = Data.map(item => item.name).indexOf("Nick");
var index = Data.findIndex(item => item.name == "John")
Which is a simplified version of:
var index = Data.findIndex(function(item){ return item.name == "John"})
From mozilla.org:
The findIndex() method returns the index of the first element in the array that satisfies the provided testing function. Otherwise -1 is returned.
Only way known to me is to loop through all array:
var index = -1;
for(var i=0; i<Data.length; i++)
if(Data[i].name === "John") {
index = i;
break;
}
Or case insensitive:
var index = -1;
for(var i=0; i<Data.length; i++)
if(Data[i].name.toLowerCase() === "john") {
index = i;
break;
}
On result variable index contain index of object or -1 if not found.
A prototypical way
(function(){
if (!Array.prototype.indexOfPropertyValue){
Array.prototype.indexOfPropertyValue = function(prop, value){
for (var index = 0; index < this.length; index++){
if (this[index][prop]){
if (this[index][prop] == value){
return index;
}
}
}
return -1;
}
}
})();
// Usage:
var Data = [
{id_list:1, name:'Nick', token:'312312'}, {id_list:2, name:'John', token:'123123'}];
Data.indexOfPropertyValue('name', 'John'); // Returns 1 (index of array);
Data.indexOfPropertyValue('name', 'Invalid name') // Returns -1 (no result);
var indexOfArray = Data.indexOfPropertyValue('name', 'John');
Data[indexOfArray] // Returns the desired object.
you can use filter method
const filteredData = data.filter(e => e.name !== 'john');
Just go through your array and find the position:
var i = 0;
for(var item in Data) {
if(Data[item].name == 'John')
break;
i++;
}
alert(i);
let indexOf = -1;
let theProperty = "value"
let searchFor = "something";
theArray.every(function (element, index) {
if (element[theProperty] === searchFor) {
indexOf = index;
return false;
}
return true;
});
collection.findIndex(item => item.value === 'smth') !== -1
You can use Array.sort using a custom function as a parameter to define your sorting mechanism.
In your example, it would give:
var Data = [
{id_list:1, name:'Nick',token:'312312'},{id_list:2,name:'John',token:'123123'}
]
Data.sort(function(a, b){
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
});
alert("First name is : " + Data[0].name); // alerts 'John'
alert("Second name is : " + Data[1].name); // alerts 'Nick'
The sort function must return either -1 if a should come before b, 1 if a should come after b and 0 if both are equal. It's up to you to define the right logic in your sorting function to sort the array.
Missed the last part of your question where you want to know the index. You would have to loop through the array to find that as others have said.
This might be useful:
function showProps(obj, objName) {
var result = "";
for (var i in obj)
result += objName + "." + i + " = " + obj[i] + "\n";
return result;
}
I copied this from Working with objects.
Use a small workaround:
Create a new array with names as indexes. After that all searches will use indexes. So, only one loop. After that you don't need to loop through all elements!
var Data = [
{id_list:1, name:'Nick',token:'312312'},{id_list:2,name:'John',token:'123123'}
]
var searchArr = []
Data.forEach(function(one){
searchArr[one.name]=one;
})
console.log(searchArr['Nick'])
http://jsbin.com/xibala/1/edit
Live example.
I extended Chris Pickett's answer, because in my case I needed to search deeper than one attribute level:
function findWithAttr(array, attr, value) {
if (attr.indexOf('.') >= 0) {
var split = attr.split('.');
var attr1 = split[0];
var attr2 = split[1];
for(var i = 0; i < array.length; i += 1) {
if(array[i][attr1][attr2] === value) {
return i;
}
}
} else {
for(var i = 0; i < array.length; i += 1) {
if(array[i][attr] === value) {
return i;
}
}
};
};
You can pass 'attr1.attr2' into the function.
Use this:
Data.indexOf(_.find(Data, function(element) {
return element.name === 'John';
}));
It is assuming you are using Lodash or Underscore.js.
var fields = {
teste:
{
Acess:
{
Edit: true,
View: false
}
},
teste1:
{
Acess:
{
Edit: false,
View: false
}
}
};
console.log(find(fields,'teste'));
function find(fields,field) {
for(key in fields) {
if(key == field) {
return true;
}
}
return false;
}
If you have one Object with multiple objects inside, if you want know if some object are include on Master object, just use find(MasterObject, 'Object to Search'). This function will return the response if it exists or not (TRUE or FALSE). I hope to help with this - can see the example on JSFiddle.
If you want to get the value of the property token then you can also try this:
let data=[
{ id_list: 1, name: 'Nick', token: '312312' },
{ id_list: 2, name: 'John', token: '123123' },
]
let resultingToken = data[_.findKey(data,['name','John'])].token
where _.findKey is a Lodash function.
You can use findIndex in Lodash library.
Example:
var users = [
{ 'user': 'barney', 'active': false },
{ 'user': 'fred', 'active': false },
{ 'user': 'pebbles', 'active': true }
];
_.findIndex(users, function(o) { return o.user == 'barney'; });
// => 0
// The `_.matches` iteratee shorthand.
_.findIndex(users, { 'user': 'fred', 'active': false });
// => 1
// The `_.matchesProperty` iteratee shorthand.
_.findIndex(users, ['active', false]);
// => 0
// The `_.property` iteratee shorthand.
_.findIndex(users, 'active');
// => 2
Alternatively to German Attanasio Ruiz's answer, you can eliminate the second loop by using Array.reduce() instead of Array.map();
var Data = [
{ name: 'hypno7oad' }
]
var indexOfTarget = Data.reduce(function (indexOfTarget, element, currentIndex) {
return (element.name === 'hypno7oad') ? currentIndex : indexOfTarget;
}, -1);
Maybe the Object.keys, Object.entries, and Object.values methods might help.
Using Underscore.js:
var index = _.indexOf(_.pluck(item , 'name'), 'Nick');

Categories