Daily-Interview-Question icon indicating copy to clipboard operation
Daily-Interview-Question copied to clipboard

第 162 题:实现对象的 Map 函数类似 Array.prototype.map

Open NieZhuZhu opened this issue 3 years ago • 38 comments

列如:

// 实现一个 map 函数
const targetData = {
  a: 2,
  b: 3,
  c: 4,
  d: 5
};
const objMap = (obj, fn) => {
  if (typeof fn !== "function") {
    throw new TypeError(`${fn} is not a function !`);
  }
  return JSON.parse(JSON.stringify(obj, fn));
};
objMap(targetData, (key, value) => {
  if (value % 2 === 0) {
    return value / 2;
  }
  return value;
});
// {a: 1, b: 3, c: 2, d: 5}

参考:

你不知道的 JSON.stringify() 的威力

NieZhuZhu avatar Nov 01 '20 10:11 NieZhuZhu

列如:

// 实现一个 map 函数
const targetData = {
  a: 2,
  b: 3,
  c: 4,
  d: 5
};
const objMap = (obj, fn) => {
  if (typeof fn !== "function") {
    throw new TypeError(`${fn} is not a function !`);
  }
  return JSON.parse(JSON.stringify(obj, fn));
};
objMap(targetData, (key, value) => {
  if (value % 2 === 0) {
    return value / 2;
  }
  return value;
});
// {a: 1, b: 3, c: 2, d: 5}

参考:

你不知道的 JSON.stringify() 的威力

这个实现有问题,首先JSON.stringify(obj, fn)中第一次传入fn中的参数为 ""和对象本身,从第二次开始才会传入key和val,所以应该加上条件处理。

(() => {

    Object.prototype._map = function (fn, oThis = null) {
        if (typeof fn !== 'function') {
            throw new TypeError(`${fn} is not a function !`)
        }
        return JSON.parse(JSON.stringify(this, (key, val) => {
            if (key) {
                return fn.call(oThis, key, val, this)
            } else {
                return val
            }
        }))
    }
    // 用例
    let obj = {
        a: 2,
        b: 3,
        c: 4,
        d: 5
    };
    let _obj = obj._map((key, val, o) => {
        return ++val
    })
    console.log(_obj);
})();

Mrcxt avatar Nov 03 '20 07:11 Mrcxt

列如:

// 实现一个 map 函数
const targetData = {
  a: 2,
  b: 3,
  c: 4,
  d: 5
};
const objMap = (obj, fn) => {
  if (typeof fn !== "function") {
    throw new TypeError(`${fn} is not a function !`);
  }
  return JSON.parse(JSON.stringify(obj, fn));
};
objMap(targetData, (key, value) => {
  if (value % 2 === 0) {
    return value / 2;
  }
  return value;
});
// {a: 1, b: 3, c: 2, d: 5}

参考: 你不知道的 JSON.stringify () 的威力

这个实现有问题,首先 JSON.stringify (obj, fn) 中第一次传入 fn 中的参数为 "" 和对象本身,从第二次开始才会传入 key 和 val,所以应该加上条件处理。

(() => {

    Object.prototype._map = function (fn) {
        if (typeof fn !== 'function') {
            throw new TypeError(`${fn} is not a function !`)
        }
        return JSON.parse(JSON.stringify(this, (key, val) => {
            if (key) {
                return fn.call(this, key, val)
            } else {
                return val
            }
        }))
    }
    // 用例
    let obj = {
        a: 2,
        b: 3,
        c: 4,
        d: 5
    };
    let _obj = obj._map((key, val) => {
        return ++val
    })
    console.log(_obj);
})();

对的,虽然不会被 JSON.stringify 返回但是做个判断总是好的

NieZhuZhu avatar Nov 03 '20 07:11 NieZhuZhu

Object.prototype.map= function(cb) {
    const obj = this
    const result = {}
    for(key in obj) {
        if (obj.hasOwnProperty(key)) {
            const item = cb(key, obj[key])
            result[key] = item
        }
    }
    return result
}
const test1 = {
  a: 2,
  b: 3,
  c: 4,
  d: 5
};
const r1 = test1.map(() => {
   if (value % 2 === 0) {
    return value / 2;
  }
  return value;
})
// r1 :  {a: 1, b: 3, c: 2, d: 5}

const test2 = {
 a: 2,
 b: 3,
 c: 4,
 d: 5
};
const r2 = test2.map((key, val) => {
  return ++val
})
// r2: {a: 3, b: 4, c: 5, d: 6}

Cecilxx avatar Nov 09 '20 03:11 Cecilxx

const targetData = {
  a: 2,
  b: 3,
  c: 4,
  d: 5
};

Object.prototype.map = function(fn) {
  const res = {}
  for(e in this) {
    if(this.hasOwnProperty(e)) {
      res[e] = fn(this[e])
    }
  }
  return res
}

const p = targetData.map(e => e + 1)
console.log(p)

FatDoge avatar Nov 10 '20 09:11 FatDoge

Object.prototype.map = function(fn) {
  const deepclone = JSON.parse(JSON.stringify(this));
	return Object.keys(deepclone).reduce((result, key, index) => {
  	result[key] = fn(deepclone[key], key, index);
    return result;
  }, {})
}

const obj = {
	a: 1,
  b: 2,
  c: 3,
}
const newObj = obj.map((value, key, index) => ({ [key]: value + ',,,' }));
console.log(newObj);

hcc0007 avatar Nov 10 '20 15:11 hcc0007

为什么怎么多人搞深克隆🙄

参考自JavaScript Array map() 定义与用法 https://www.runoob.com/jsref/jsref-map.html

Object.prototype.map = function (handleFn,thisValue) {
    const obj = this;
    let res = {};
    for (let prop in obj) {
        if (obj.hasOwnProperty(prop)) {
            res[prop] = handleFn.call(thisValue,obj[prop], prop,obj);
        }
    }
    return res;
};
// 测试用例
var obj = {
    name: 'sunny',
    sex: 'man'
};
var res = obj.map(function(val, prop,obj){
    console.log(this);
    console.log(val, prop,obj);
    return prop + '--' + val;
}, {name: 'thisthis'});
console.log('res:',res);

JianJroh avatar Nov 24 '20 02:11 JianJroh

Object.prototype.map = function(handleFn){ return Object.keys(this).reduce((newObj, key) => { return newObj[key] = handleFn(this[key], key, this); }, {}) }

getOnce avatar Nov 24 '20 10:11 getOnce

function objMap(source, cb) {
  return Object.keys(source).reduce((pre, key) => {
    pre[key] = cb(key, source[key], source);
    return pre;
  }, {});


linrunzheng avatar Dec 04 '20 09:12 linrunzheng

Object.prototype.map = function (fn) {
    let result = {}
    for (const key in this) {
        if (this.hasOwnProperty(key)) {
            const element = this[key]
            result = fn.call(this, key, element)
        }
    }
    return result
}


let res = obj.map((key, val) => {
    return {
        key, val
    }
})

blank1u avatar Dec 13 '20 09:12 blank1u

Object.prototype.map = function (iteratee) {
  const result = {}
  for (const key of Object.keys(source)) {
    result[key] = iteratee.call(this, source[key], key)
  }
  return result
}

zfowed avatar Jan 25 '21 08:01 zfowed

class MyArr {
    constructor(arr) {
        this.arr = arr;
    }
    map(cb) {
        let newArr = [];
        for(var i =0 ; i < this.arr.length; i++) {
           newArr[i] = cb(this.arr[i]);
        }
        return newArr;
    }
}
let arr = new MyArr([1,2,3,4,5])
arr.map((item) => {
    retrun item +1;
})

Jialufeng avatar Mar 24 '21 08:03 Jialufeng

const map = Symbol("map");
Object.prototype[map] = function (callback) {
  const obj = {};
  for (let key in this) {
    obj[key] = callback(this[key], key, this);
  }
  return obj;
};
console.log({ a: 1, b: 2, c: 3 }[map](v => v + 1));

moonyoulove avatar Mar 29 '21 14:03 moonyoulove

Object.prototype.map = function(callbackfn) {
    Object.keys(this).map((value, index, array) => {
        callbackfn(this[value], value, this);
    });
}

const obj = new Object({
    a: 1,
    b: 2,
    c: 3,
    d: 4,
    e: 5
});
obj.map((value, index, array) => {
    console.log(value, index, array);
})

Z1hgq avatar Mar 31 '21 03:03 Z1hgq

Object.prototype._map = function (fn) {
  const obj = this
  let result = {}
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      var res = fn(obj[key], key, obj)
      result[key] = res
    }
  }
  return result
}
var o = { a: 1, b: 2 }
o._map((value, key, source) => {
  return 1
})
// {a: 1, b: 1}

MrLeihe avatar Apr 14 '21 15:04 MrLeihe


function objMap(obj,func) {
    if(typeof func  !== 'function') {
        throw new Error(`${func} must be a function`)
    }
    const ctor = obj.__proto__.constructor
    let res = new ctor();
    for(let prop of Object.getOwnPropertyNames(obj)) {
        res[prop] = func(obj[prop])
    }
    return res
}

function Person(a,b,c,d) {
    this.a = a
    this.b = b
    this.c = c
    this.d = d
}

Person.prototype.e = 'prototype value'

let origin  = new Person(1,2,3,4)
let dst = objMap(origin,(a)=>a*a)
console.log(origin,dst)
// Person { a: 1, b: 4, c: 9, d: 16 }
console.log(origin.__proto__ === dst.__proto__) // true
console.log(dst instanceof Person) // true
console.log(dst.e) // prototype value


zzz1220 avatar Apr 16 '21 10:04 zzz1220

转字符串再转对象,性能不好

satrong avatar Apr 23 '21 15:04 satrong

function objectMap(obj, fn) {
  // Not check type yet
  //
  return Object.entries(obj)
    .map(([key, value]) => [key, fn(key, value)])
    .reduce((acc, [key, value]) => {
      acc[key] = value;
      return acc;
    }, {});
}

nomyfan avatar May 08 '21 15:05 nomyfan

function map(fn,scope){
   var result=[];
var i=0;
   var realScope=scope || this
   var len=this.length;
  while(i<len){
result[i]=fn.call(realScope,this[i],i)
i++;
}

return result;
}
Array.prototype.map=map

jumplee avatar May 12 '21 03:05 jumplee

/**
 *
 * @param {Function} callbackFn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the object.
 * @param {Record<string, any>} thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
 * @returns {Record<string, any>}
 */
Object.prototype.map = function (callbackFn, thisArg) {
  if (typeof callbackFn !== "function") {
    throw new TypeError(`${callbackFn} is not a function`);
  }
  const ret = {};
  const hasOwn = Object.prototype.hasOwnProperty;
  for (let key in this) {
    hasOwn.call(this, key) &&
      (ret[key] = callbackFn.call(thisArg, this[key], key, this));
  }
  return ret;
};

peterczg avatar May 13 '21 15:05 peterczg

var testObj = {
  a: 1,
  b: 2,
  c: { d: 1 }
}
Object.prototype.map = function (callbackfn) {
  for (var [key, val] of Object.entries(this)) {
    callbackfn(key, val)
  }
}

testObj.map((key, val) => {
  console.log(key, val)
})

Y-J-H avatar May 19 '21 11:05 Y-J-H

Object.prototype.map = function(callbackFn, thisArg) {
    if (typeof callbackFn !== "function")
       throw new TypeError(`${callbackFn} is not a function`);
    if (Object.prototype.toString.call(thisArg) != "[object Object]")
        thisArg = this;
    for (let key in thisArg) {
        if (thisArg.hasOwnProperty(key)) {
            thisArg[key] = callbackFn(thisArg[key], key, thisArg);
        }
    }
    return thisArg;
}

sytclh avatar Jun 03 '21 03:06 sytclh

var obj = { a: 1 } function objMap(fn) { var obj1 = {}; Object.keys(this).forEach((i) => { obj1[i] = fn(this[i]) }); return obj1 } Object.prototype.map = objMap; console.log(obj.map((i) => i + 1), obj) // {a:2} {a:1}

shengdan19 avatar Jun 03 '21 08:06 shengdan19

分析实现的效果

Array的map语法如下

var new_array = arr.map(function callback(currentValue[, index[, array]]) {
 // Return element for new_array 
}[, thisArg])

结论

  • map的入参为回调函数,需要做函数类型判断
  • map的返回值为一个经过回调函数处理后的对象
  • 回调函数的入参分别是:valueindex、原始数据
  • 回调函数的返回值会作为最终对象的value值,若无返回则为undefined

Object.prototype.map实现

// 实现对象的 Map 函数类似 Array.prototype.map
Object.prototype.map = function (fn) {
  if (typeof fn !== "function") {
    throw new TypeError(`${fn} is not a function`);
  }
  const result = {}
  for (const [key, value] of Object.entries(this)) {
    result[key] = fn(value, key, this)
  }
  return result
}

function _isEqual(obj1, obj2) {
  // 基于测试数据是简单的数据
  // 采用JSON.stringify()进行一致性判别
  return JSON.stringify(obj1) === JSON.stringify(obj2)
}

const testObj = {
  name: 'Nika',
  age: 18
}

// map处理后的返回
const mapRes = testObj.map((value, key, originObj) => {
  if (key === 'name') {
    return `My name is ${value}`
  }
})

// map后的目标结果
const target = {
  name: `My name is ${testObj.name}`,
  age: undefined
}

console.assert(_isEqual(mapRes, target))

NikaSSL avatar Jun 26 '21 03:06 NikaSSL

Array.prototype._map = function (fn) {
  const arr = this;
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    const ele = arr[i];
    res[i] = fn.call(globalThis, ele, i, arr);
  }
  return res;
}

poppydani avatar Jul 07 '21 03:07 poppydani

Object.prototype.map = function (fn, _self) {
    let keys = Object.keys(this),
        values = [];
    for (let i = 0; i < keys.length; i++) {
        values.push(fn.call(_self, this[keys[i]], i))
    }
    return values;
}

let obj = {
    key: 'lihai',
    value: "map"
}

console.log(obj.map((val, index) => {
    console.log(val, index);
    return val;
}));

CatsAndMice avatar Aug 02 '21 00:08 CatsAndMice

Object.prototype.map = function(fn) {
  let new_obj = {}
  for (const p in this) {
    if (this.hasOwnProperty(p)) {
      console.log(`${p} = ${this[p]}`)
      new_obj[p] = fn(this[p])
    }
  }
  return new_obj
}
let obj = {
  x: 1,
  y: 2
}

const obj2 = obj.map(x => x * 2)
console.log(obj2)
//output : { x: 2, y: 4 }

shaun2099 avatar Sep 20 '21 14:09 shaun2099

    Object.prototype.map = function (fn) {
        const res = {};
        for (const key in this) {
            // 不加这句会把map添加进去
            if (Object.hasOwnProperty.call(this, key)) {
                const value = this[key];
                res[key] = fn(value, key, this);
            }
        }
        return res;
    };

    const testObj = {
        name: "Nika",
        age: 18,
    };

    const mapObj = testObj.map((value, key) => {
        if (key === "name") {
            return `My name is ${value}`;
        }
    });

    console.log(testObj);
    console.log(mapObj);

beilo avatar Dec 01 '21 07:12 beilo

MDN的map polyfill

// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: https://es5.github.io/#x15.4.4.19
if (!Array.prototype.map) {

  Array.prototype.map = function(callback/*, thisArg*/) {

    var T, A, k;

    if (this == null) {
      throw new TypeError('this is null or not defined');
    }

    // 1. Let O be the result of calling ToObject passing the |this|
    //    value as the argument.
    var O = Object(this);

    // 2. Let lenValue be the result of calling the Get internal
    //    method of O with the argument "length".
    // 3. Let len be ToUint32(lenValue).
    var len = O.length >>> 0;

    // 4. If IsCallable(callback) is false, throw a TypeError exception.
    // See: https://es5.github.com/#x9.11
    if (typeof callback !== 'function') {
      throw new TypeError(callback + ' is not a function');
    }

    // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
    if (arguments.length > 1) {
      T = arguments[1];
    }

    // 6. Let A be a new array created as if by the expression new Array(len)
    //    where Array is the standard built-in constructor with that name and
    //    len is the value of len.
    A = new Array(len);

    // 7. Let k be 0
    k = 0;

    // 8. Repeat, while k < len
    while (k < len) {

      var kValue, mappedValue;

      // a. Let Pk be ToString(k).
      //   This is implicit for LHS operands of the in operator
      // b. Let kPresent be the result of calling the HasProperty internal
      //    method of O with argument Pk.
      //   This step can be combined with c
      // c. If kPresent is true, then
      if (k in O) {

        // i. Let kValue be the result of calling the Get internal
        //    method of O with argument Pk.
        kValue = O[k];

        // ii. Let mappedValue be the result of calling the Call internal
        //     method of callback with T as the this value and argument
        //     list containing kValue, k, and O.
        mappedValue = callback.call(T, kValue, k, O);

        // iii. Call the DefineOwnProperty internal method of A with arguments
        // Pk, Property Descriptor
        // { Value: mappedValue,
        //   Writable: true,
        //   Enumerable: true,
        //   Configurable: true },
        // and false.

        // In browsers that support Object.defineProperty, use the following:
        // Object.defineProperty(A, k, {
        //   value: mappedValue,
        //   writable: true,
        //   enumerable: true,
        //   configurable: true
        // });

        // For best browser support, use the following:
        A[k] = mappedValue;
      }
      // d. Increase k by 1.
      k++;
    }

    // 9. return A
    return A;
  };
}

antyoxydant avatar Dec 20 '21 06:12 antyoxydant

const targetData = {
  a: 2,
  b: 3,
  c: 4,
  d: 5,
};
Object.prototype.objMap = function (fn) {
  if (typeof fn !== "function") {
    throw new TypeError(`${fn} is not a function !`);
  }
  return JSON.parse(
    JSON.stringify(this, (key, value) => {
      if (!key) {
        return value;
      }
      return fn.call(null, key, value);
    })
  );
};

const newData = targetData.objMap((key, value) => {
  if (value % 2 === 0) {
    return value / 2;
  }
  return value;
});

LiuSandy avatar Dec 30 '21 10:12 LiuSandy

**const objectMap = (obj, operation) => {
  if (typeof operation !== "function") {
    throw new TypeError(`${operation} is not a function !`);
  }

  const res = Object.fromEntries(
    Object.entries(obj).map(([key, val]) => [key, operation(val)])
  );
  return res;
};**

Verahuan avatar Mar 10 '22 04:03 Verahuan

Array.prototype.map = function (callback, thisArg) {
  if (!this) throw TypeError('this 未定义或者为 null');
  const bindThis = thisArg ?? null;
  
  const arrObj = Object(this);
  const len = arrObj.length >>> 0;
  
  let newArr = [];
  let idx = 0;
  while(idx < len) {
    const item = arrObj[idx];
    if (idx in arrObj) {
      const res = callback.call(bindThis, item, idx, arrObj);
      newArr[idx] = res;
    }
    idx++;
  }
  
  return newArr;
}

const a = [1, 4, 9, 16];

const a1 = a.map(x => x * 2);
console.log(a1);

itLeeyw avatar May 19 '22 09:05 itLeeyw

Object.prototype.map = function (callbackFn) {
  if (typeof callbackFn === 'undefined') {
    throw new TypeError(`${callbackFn} is not a function`);
  }
  const res = {};
  Object.entries(this).forEach(([key, value]) => {
    res[key] = callbackFn(value, key);
  });
  return res;
};

const obj = {
  a: 1,
  b: 100,
  c: 'human',
  d: 'earth',
  e: true,
};

const transformedObj = obj.map((value, key) => {
  if (typeof value === 'number') return value + 2;
  if (typeof value === 'string') return `${value} is awesome`;
  if (typeof value === 'boolean') return !value;
  return value;
});

console.log(transformedObj);
// {
//   a: 3,
//   b: 102,
//   c: 'human is awesome',
//   d: 'earth is awesome',
//   e: false
// }

lucaslmlok avatar Jun 20 '22 16:06 lucaslmlok